6

我在重载时理解方法的变化时遇到了一点问题。

虽然由于返回类型的协方差,这非常有效

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

4

4 回答 4

12

这里有两件事:

  1. 函数和方法不是一回事
  2. 方法的参数类型不是多态的

您的tester方法是一种方法,而不是Function1. 可以使用下划线语法将其提升为函数:

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

这个函数的输入类型是逆变的。(值得一提的是,函数不能被参数化,也值得一提的是,我必须有一个Fooor的实例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定义了一个函数成为另一个函数的子类型(因此可分配给给定类型的引用)的含义。
  • 方法可以在子类上被覆盖,并且语言规范概述了何时发生这种覆盖的规则。
于 2010-11-28T14:08:24.423 回答
4
于 2010-11-28T15:54:51.687 回答
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
于 2010-11-28T14:36:04.050 回答
-1

那么在你的第二个例子中,tester()in的签名Test声明了一个Fasel参数,但重写的签名FooTest tester()是用Blaas 参数声明的。由于FaselBlaextends 层次结构的子类型,因此这可能是错误的。

于 2010-11-28T14:00:35.927 回答