Scala 函数变体和重写

发布于 2024-10-05 00:35:00 字数 628 浏览 0 评论 0原文

我在理解重载时方法的差异时遇到了一些问题。

虽然由于返回类型的协变性,这种方法可以完美地工作,

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
}

我在这里犯了什么错?有什么指点吗?

问候, 雷鸟

I'm having a little problem understanding variance of methods when overloading.

While this perfectly works due to covariance in the return type

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                                      
}

this one fails even though functions are contravariant in their
parameter types.

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
}

What am I getting wrong here? Any pointers?

Regards,
raichoo

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

时间你老了 2024-10-12 00:35:00

这里发生了两件事:

  1. 函数和方法不是同样的事情
  2. 方法的参数类型不是多态的。

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

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

该函数的输入类型将是逆变的。 (不过,值得一提的是,函数无法参数化,并且还值得一提的是,我必须拥有 FooFooTest 的实例才能获取 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 上的方差注释定义了它对于一个事物的含义函数成为另一个函数的子类型(因此可分配给给定类型的引用)。
  • 可以在子类上重写方法,并且语言规范概述了何时发生此类重写的规则。

There are two things going on here:

  1. A function and a method are not the same thing
  2. Methods are not polymorphic in their parameters' types

Your tester method is a method, not a Function1. It can be lifted into a function using the underscore syntax:

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

This function will be contra-variant in its input type. (It's worth saying, however, that functions cannot be parameterized and also worth saying that I had to have an instance of Foo or FooTest in order to get a function object for the tester method. This of course follows from the first observation!)

A function is an object, it cannot be overridden as that makes no sense. Methods can be overridden. However, as I say above, the overriding is not polymorphic in the method's parameter types. So for example:

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

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

The two methods in my example above are two separate methods: dynamic dispatch works only on the method target (i.e. the object on which it is being called).

In the above, example, if you remove the override declaration, the code will compile. If you run the following:

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

This is because, although both methods are called foo, they are completely different methods (i.e. I have overloaded foo, not overridden it). It's best understood as being that a method's arguments' (incl their types) form part of that method's unique name. One method overrides another only if they have exactly the same name.


Essentially you have confused what are two separate and un-related things in your question, which I will put down for clarity:

  • The variance annotations on Function1 define what it means for one function to be a subtype of another (and hence assignable to a reference of a given type).
  • Methods can be overridden on subclasses and the language specification outlines rules for when such overriding takes place.
来日方长 2024-10-12 00:35:00

规范的相关片段:

方法类型

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

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

覆盖

C 类的成员 M匹配 (§5.1.3) 非私有成员 M′C 基类的 code> 据说会覆盖该成员。在这种情况下,重写成员 M 的绑定必须包含(第 3.5.2 节)被重写成员 M' 的绑定。

一致性

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


包含

类类型 C 的某些复合类型中的声明或定义包含某些复合类型或类类型 C' 中同名的另一个声明,如果以下之一以下保留。

  • 定义类型为 T 的名称 x 的值声明或定义包含定义类型为 T'x 的值或方法声明,前提是 T < ;:T′.

The relevant snippets of the spec:

Method Types

A method type is denoted internally as (Ps)U , where (Ps) is a sequence of parameter names and types (p1 :T1,...,pn :Tn) for some n≥0 and U is a (value or method) type. This type represents named methods that take arguments named p1, ..., pn of types T1,...,Tn and that return a result of type U.

Method types do not exist as types of values. If a method name is used as a value, its type is implicitly converted to a corresponding function type (§6.26).

Overriding

A member M of class C that matches (§5.1.3) a non-private member M′ of a base class of C is said to override that member. In this case the binding of the overriding member M must subsume (§3.5.2) the binding of the overridden member M′.

Conformance

If Ti ≡ Ti′ for i = 1, ..., n and U conforms to U′ then the method type (p1 : T1,...,pn :Tn)U conforms to (p1′ :T1′,...,pn′ :Tn′)U′.

Subsumes

A declaration or definition in some compound type of class type C subsumes another declaration of the same name in some compound type or class type C′ , if one of the following holds.

  • A value declaration or definition that defines a name x with type T subsumes a value or method declaration that defines x with type T′, provided T <: T′.
梨涡 2024-10-12 00:35:00

您可以覆盖并将返回类型更改为子类型,但是虽然接受超类型作为参数将满足替换原则,但这是不允许的(这就像在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

You can override and change the return type to a subtype, but while accepting supertype for argument would satisfy the substitution principle, it is not allowed (this is just as in java) The reason is that you can also overload methods (several methods with same name, different arguments count and types) and your method will be considerered an overload. I guess this is mainly a question of JVM compatibility and of having a reasonable spec. Overloading already makes the scala spec rather complicated. Simply routing the overriden method to the overloaded one with the changed signature might be good enough:

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

What you can do is make a type parameter contravariant, provided in appears only in contravariant position (simplifying, appears as argument type and not as result type) but it is quite different:

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
○愚か者の日 2024-10-12 00:35:00

在第二个示例中,Test 中的 tester() 签名声明了一个 Fasel 参数,但具有 FooTest 的重写签名code> tester() 使用 Bla 作为参数进行声明。由于根据 extend 的层次结构,Fasel 是 Bla 的子类型,这可能是错误的。

Well in your second example the signature of tester() in Test declares a Fasel argument but with the overriden signature of FooTest tester() is declared with a Bla as argument. Since Fasel is a subtype of Bla by their extends hierarchy this is probably wrong.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文