2010-11-28 22 views
5

Tengo un pequeño problema para comprender la variación de métodos cuando se sobrecarga.Variación y anulación de la función Scala

Aunque esto funciona perfectamente debido a la covarianza en el tipo de retorno

class Bla 
class Fasel extends Bla 

trait Test[A] { 
def tester(): Bla = new Bla 
} 

class FooTest[A](a: A) extends Test[A] { 
override def tester(): Fasel = new Fasel          
} 

éste falla aunque las funciones son contravariant en sus tipos de parámetros.

class Bla 
class Fasel extends Bla 

trait Test[A] { 
def tester(a: Fasel): Bla = new Bla           
} 

class FooTest[A](a: A) extends Test[A] { 
override def tester(a: Bla): Fasel = new Fasel 
} 

¿Qué me estoy equivocando aquí? ¿Alguna sugerencia?

Saludos, raichoo

Respuesta

11

Hay dos cosas que suceden aquí:

  1. una función y un método are not the same thing
  2. métodos no son polimórficos en los tipos de sus parámetros

Su método tester es un método , no es Function1. Puede ser levantado en una función utilizando la sintaxis de subrayado:

val f = (new FooTest[String]).tester _ // Fasel => Bla 

Esta función será contra-variante en su tipo de entrada. (Vale la pena decir, sin embargo, que las funciones no se pueden parametrizar y también vale la pena decir que tuve que tener una instancia de Foo o FooTest para obtener un objeto de función para el método tester. Esto, por supuesto, se sigue de la primera observación!)

Una función es un objeto, no se puede anular ya que no tiene sentido. Los métodos pueden ser anulados. Sin embargo, como dije antes, la anulación no es polimórfica en los tipos de parámetros del método. Así, por ejemplo:

class A { 
    def foo(a : Any) = println("A: " + a) 
} 

class B extends A { 
    override def foo(s : String) = println("B " + s) //will not compile! 
} 

los dos métodos en el ejemplo anterior son dos métodos distintos: envío dinámico sólo funciona en el objetivo método (es decir, el objeto sobre el que está siendo llamado).

En el ejemplo anterior, si elimina la declaración override, se compilará el código. Si ejecuta el siguiente:

(new B).foo(1) //prints A 1 
(new B).foo("s") //prints B s 

Esto es debido a que, aunque ambos métodos se llaman foo, son completamente diferentes métodos (es decir, he sobrecargado foo, no se reemplaza ella).Se entiende mejor como que los argumentos de un método '(incluidos sus tipos) forman parte del nombre único de ese método. Un método prevalece sobre otro solo si tienen exactamente el mismo nombre .


Esencialmente usted ha confundido lo que son dos independiente y cosas en su pregunta, que voy a poner abajo para mayor claridad relacionados con la ONU:

  • Las anotaciones de varianza en Function1 definen lo que significa para una función es un subtipo de otro (y por lo tanto asignable a una referencia de un tipo dado).
  • Los métodos se pueden anular en las subclases y la especificación del lenguaje describe las reglas para cuando ocurre dicha anulación.
+0

¿Dónde puedo leer esto en la referencia de scala? Solo por el hecho de ser completo :) – raichoo

+2

Entendido: 3.3.1 MethodTypes Gracias :) – raichoo

+1

He agregado un enlace a una pregunta sobre la comparación de método/función. Además, el bit sobre anulación de método es parte de la definición de bytecode (es decir, la especificación de formato de archivo de clase). Aunque espero que el lenguaje Scala especifique cómo se "levanta" un método scala en un método de bytecode. –

-1

bien en su segundo ejemplo de la firma del tester() en Test declara un argumento Fasel pero con la firma de overriden FooTesttester() se declara con un Bla como argumento. Como Fasel es un subtipo de Bla por su jerarquía de extend, probablemente sea incorrecto.

+0

Te estás perdiendo el punto. Una función es contravariante en su parámetro de entrada. Por lo tanto, el OP estaba confundido acerca de la diferencia entre la anulación del método y la varianza de la función –

0

Puede anular y cambiar el tipo de retorno a un subtipo, pero aceptando al mismo tiempo supertipo para el argumento podría satisfacer el principio de sustitución, que no está permitido (esto es igual que en java) La razón es que también se puede sobrecarga métodos (varios métodos con el mismo nombre, diferentes argumentos y tipos) y su método será considerado una sobrecarga. Supongo que esto es principalmente una cuestión de compatibilidad con JVM y de tener una especificación razonable. La sobrecarga ya hace que la especificación scala sea bastante complicada. Simplemente encaminar el método sobreescrito a la sobrecarga de la firma cambiado podría ser lo suficientemente bueno:

class FooTest[A] extends Test[A] { 
    override def test(a: Fasel) : Fasel = test(a.asInstanceOf[Bla]) 
    def test(a: Bla) : Fasel = new Fasel 
} 

Lo que puede hacer es hacer una contravariant parámetro de tipo, proporcionada en sólo aparece en la posición contravariante (simplificando, aparece como tipo de argumento y no como tipo de resultado), pero es bastante diferente:

trait Test[-A] { 
    // note the - before A. 
    // You might want to constraint with -A >: Fasel 
    def tester(a: A) : Bla = new Bla 
} 

class FooTest extends Test[Bla] { 
    override def tester(a: Bla): Fasel = new Fasel 
} 

val testOfBla: Test[Bla] = new FooTest 
val testOfFasel: Test[Fasel] = testOfBla 
    // you can assign a Test[Bla] to a test[Fasel] because of the -A 
4

los fragmentos de relevantes de la especificación:

Tipos Método

Un tipo de método se denota internamente como (Ps)U, donde (Ps) es una secuencia de nombres de parámetros y tipos (p1 :T1,...,pn :Tn) para algunos n≥0 y U es un (valor o método) tipo. Este tipo representa métodos con nombre que toman argumentos llamados p1, ..., pn de los tipos T1,...,Tn y que devuelven un resultado del tipo U.

Los tipos de método no existen como tipos de valores. Si se utiliza un nombre de método como valor, su tipo se convierte implícitamente a un tipo de función correspondiente (§6.26).

Anulación de

Un miembro de M de clase C que partidos (§5.1.3) un miembro no privado M′ de una clase base de C se dice para anular ese miembro. En este caso, la vinculación del miembro predominante M debe subsumir (§3.5.2) la vinculación del miembro reemplazado M′.

Conformidad

Si Ti ≡ Ti′ para i = 1, ..., n y U ajusta a U′ entonces el tipo de método (p1 : T1,...,pn :Tn)U ajusta a (p1′ :T1′,...,pn′ :Tn′)U′.

subsume

Una declaración o definición de algún tipo de compuesto de tipo de clase C subsume otra declaración del mismo nombre en algún tipo compuesto o clase de tipo C′, si una de las siguientes bodegas.

  • una declaración de valor o definición que define un nombre x con el tipo T subsume una declaración de valor o un método que define x con el tipo T′, proporcionado T <: T′.
Cuestiones relacionadas