Scala 函数变体和重写
我在理解重载时方法的差异时遇到了一些问题。
虽然由于返回类型的协变性,这种方法可以完美地工作,
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
这里发生了两件事:
您的
tester
方法是一个方法,不是Function1
>。可以使用下划线语法将其提升为函数:该函数的输入类型将是逆变的。 (不过,值得一提的是,函数无法参数化,并且还值得一提的是,我必须拥有
Foo
或FooTest
的实例才能获取tester
方法的函数对象。这当然是从第一个观察得出的!)函数是一个对象,它不能被覆盖,因为这没有任何意义。方法可以被重写。然而,正如我上面所说,重写在方法的参数类型中不是多态的。例如:
上面示例中的两个方法是两个独立的方法:动态调度仅适用于方法目标(即调用它的对象)。
在上面的示例中,如果删除
override
声明,代码将编译。如果您运行以下命令:这是因为,虽然这两个方法都称为
foo
,但它们是完全不同的方法(即我已经重载foo
,没有覆盖它)。最好将其理解为方法的参数(包括其类型)构成该方法的唯一名称的一部分。仅当它们具有完全相同的名称时,一种方法才会覆盖另一种方法。本质上,您混淆了问题中两个独立且不相关的事物,为了清楚起见,我将把它们写下来:
Function1
上的方差注释定义了它对于一个事物的含义函数成为另一个函数的子类型(因此可分配给给定类型的引用)。There are two things going on here:
Your
tester
method is a method, not aFunction1
. It can be lifted into a function using the underscore syntax: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
orFooTest
in order to get a function object for thetester
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:
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:This is because, although both methods are called
foo
, they are completely different methods (i.e. I have overloadedfoo
, 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:
Function1
define what it means for one function to be a subtype of another (and hence assignable to a reference of a given type).规范的相关片段:
方法类型
覆盖
一致性
包含
The relevant snippets of the spec:
Method Types
Overriding
Conformance
Subsumes
您可以覆盖并将返回类型更改为子类型,但是虽然接受超类型作为参数将满足替换原则,但这是不允许的(这就像在java中一样)原因是您还可以重载方法(多个具有相同的方法)名称、不同的参数数量和类型),并且您的方法将被视为重载。我想这主要是 JVM 兼容性和合理规范的问题。重载已经使 scala 规范变得相当复杂。简单地将重写方法路由到具有更改签名的重载方法可能就足够了:
您可以做的是使类型参数逆变,提供的仅出现在逆变位置(简化,显示为参数类型而不是结果类型)但是这是完全不同的:
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:
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:
在第二个示例中,
Test
中的tester()
签名声明了一个Fasel
参数,但具有FooTest
的重写签名code>tester()
使用Bla
作为参数进行声明。由于根据extend
的层次结构,Fasel 是Bla
的子类型,这可能是错误的。Well in your second example the signature of
tester()
inTest
declares aFasel
argument but with the overriden signature ofFooTest
tester()
is declared with aBla
as argument. SinceFasel
is a subtype ofBla
by theirextend
s hierarchy this is probably wrong.