Inheritance Scala函数的方差和重写

Inheritance Scala函数的方差和重写,inheritance,scala,overriding,variance,Inheritance,Scala,Overriding,Variance,当重载时,我在理解方法的变化方面有点问题 由于返回类型中的协方差,这一点非常有效 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(): 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

在您的第二个示例中,
Test
中的
tester()
的签名声明了一个
Fasel
参数,但是使用
FooTest
tester()
的覆盖签名声明的参数是
Bla
。由于
Fasel
是其
extend
s层次结构的
Bla
的一个子类型,这可能是错误的。

这里有两件事:

  • 函数与方法
  • 方法的参数类型不是多态的
  • 您的
    测试仪
    方法是一种方法,而不是
    功能1
    。可以使用下划线语法将其提升到函数中:

    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!
    }
    
    上面示例中的两个方法是两个独立的方法:动态分派仅对方法目标(即调用它的对象)起作用

    在上面的示例中,如果删除
    覆盖
    声明,代码将编译。如果运行以下命令:

    (new B).foo(1)   //prints A 1
    (new B).foo("s") //prints B s
    
    这是因为,尽管这两种方法都被称为
    foo
    ,但它们是完全不同的方法(即,我重载了
    foo
    ,而不是重写它)。最好理解为方法的参数(包括其类型)构成该方法唯一名称的一部分。一个方法只有在它们具有完全相同的名称时才重写另一个方法


    基本上,你已经混淆了你问题中的两个独立和不相关的事物,为了清楚起见,我将把它们记下来:

    • Function1
      上的方差注释定义了一个函数作为另一个函数的子类型(因此可分配给给定类型的引用)的含义
    • 方法可以在子类上重写,并且语言规范概述了此类重写发生的规则

    您可以重写返回类型并将其更改为子类型,但尽管接受参数的超类型将满足替换原则,但这是不允许的(这与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
    

    规范的相关片段:

    方法类型

    方法类型在内部表示为
    (Ps)U
    ,其中
    (Ps)
    是一些
    n的参数名称和类型的序列
    (p1:T1,…,pn:Tn)
    ≥0
    U
    是(值或方法)类型。此类型表示命名方法,这些方法采用类型为
    T1,…,Tn
    的名为
    p1,…,pn
    的参数,并返回类型为
    U
    的结果

    方法类型不作为值类型存在。如果方法名用作值,则其类型将隐式转换为相应的函数类型(§6.26)

    覆盖

    C
    的成员
    M
    C
    基类的非私有成员
    M′
    匹配(§5.1.3),称其覆盖该成员。在这种情况下,覆盖成员
    M
    的约束必须包含(§3.5.2)覆盖成员
    M′
    的约束

    合规性

    如果
    Ti≡ Ti′
    对于
    i=1,…,n
    U
    符合
    U′
    ,则方法类型
    (p1:T1,…,pn:Tn)U
    符合
    (p1′:T1′,,pn′:Tn′)U′

    包含

    类类型
    C
    的某个复合类型中的声明或定义包含了某个复合类型或类类型
    C′
    中相同名称的另一个声明,前提是满足以下条件之一


    • 定义类型为T的名称x的值声明或定义包含定义类型为
      T′
      x
      的值或方法声明,前提是
      T在scala参考中哪里可以阅读?为了完整性:)明白了:3.3.1方法类型谢谢:)我添加了一个关于方法/函数比较的SO问题的链接。此外,关于方法重写的部分是字节码定义的一部分(即类文件格式规范)。虽然我希望Scala语言规范详细说明Scala方法是如何“提升”为字节码方法的,但你没有抓住要点。函数的输入参数是反变的。因此OP对方法重写和函数变化之间的区别感到困惑