我在解决重载时方法的方差方面遇到了一些问题。
虽然由于返回类型中的协方差而完美地起作用
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
}
即使功能在其中是逆变的,这个也会失败 参数类型。
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
}
我在这里弄错了什么?有什么指针吗?
此致 raichoo
答案 0 :(得分:11)
这里有两件事:
您的tester
方法是一种方法,不是Function1
。可以使用下划线语法将其提升为函数:
val f = (new FooTest[String]).tester _ // Fasel => Bla
此功能在其输入类型中具有反变量。 (值得一提的是,函数无法参数化并且值得说我必须有一个Foo
或FooTest
的实例才能获得一个函数对象tester
方法。这当然是从第一次观察开始的!)
一个函数是一个对象,它不能被覆盖因为没有意义。方法可以被覆盖。但是,正如我上面所说,覆盖在方法的参数类型中不是多态的。例如:
class A {
def foo(a : Any) = println("A: " + a)
}
class B extends A {
override def foo(s : String) = println("B " + s) //will not compile!
}
上面示例中的两个方法是两个独立的方法:动态调度仅适用于方法目标(即调用它的对象)。
在上面的示例中,如果删除override
声明,代码将被编译。如果您运行以下内容:
(new B).foo(1) //prints A 1
(new B).foo("s") //prints B s
这是因为,尽管两种方法都称为foo
,但它们是完全不同的方法(即我重载 foo
,而不是重写它)。最好的理解是方法的参数'(包括它们的类型)构成该方法的唯一名称的一部分。一种方法只有在具有完全相同的名称时才会覆盖另一种方法。
基本上你已经混淆了你的问题中两个单独和不相关的东西,为了清楚起见,我将放下这些东西:
Function1
上的方差注释定义了一个函数作为另一个函数的子类型意味着什么(因此可赋值到给定类型的引用)。答案 1 :(得分:4)
规范的相关摘要:
方法类型
方法类型在内部表示为
(Ps)U
,其中(Ps)
是参数名称序列,某些(p1 :T1,...,pn :Tn)
和n≥0
的类型U
是(值或方法)类型。此类型表示命名方法,这些方法接受类型为p1, ..., pn
的名为T1,...,Tn
的参数,并返回类型为U
的结果。方法类型不作为值的类型存在。如果方法名称用作值,则其类型将隐式转换为相应的函数类型(第6.26节)。
<强>重写强>
类
M
的成员C
匹配(§5.1.3)基类{{1}的非私有成员M′
据说要覆盖那个成员。在这种情况下,覆盖成员C
的绑定必须包含(§3.5.2)被覆盖成员M
的绑定。
<强>一致性强>
如果
M′
和Ti ≡ Ti′
的{{1}}符合i = 1, ..., n
,则方法类型U
符合U′
。
<强>涵括强>
类型
(p1 : T1,...,pn :Tn)U
的某种复合类型中的声明或定义包含某些复合类型或类类型(p1′ :T1′,...,pn′ :Tn′)U′
中的同名声明,如果满足下列条件之一。
- 定义名称x且类型为T的值声明或定义包含一个值或方法声明,用于定义
C
C′
,提供x
。
答案 2 :(得分:0)
你可以覆盖并将返回类型更改为子类型,但是虽然接受参数的超类型将满足替换原则,但是不允许(这与java中一样)原因是你也可以重载方法(几个具有相同名称,不同参数计数和类型的方法),您的方法将被视为过载。我想这主要是JVM兼容性和合理规范的问题。重载已经使scala规范变得相当复杂。简单地将重写方法路由到具有更改的签名的重载方法可能就足够了:
class FooTest[A] extends Test[A] {
override def test(a: Fasel) : Fasel = test(a.asInstanceOf[Bla])
def test(a: Bla) : Fasel = new Fasel
}
你可以做的是使一个类型参数逆变,只提供在逆变位置(简化,显示为参数类型而不是结果类型),但它是完全不同的:
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
答案 3 :(得分:-1)
在第二个示例中,tester()
中Test
的签名声明了Fasel
参数,但是FooTest
tester()
的覆盖签名声明为Bla
作为论据。由于Fasel
是Bla
层次结构extend
的子类型,因此这可能是错误的。