【发布时间】:2018-02-02 20:01:13
【问题描述】:
以下 trait 声明了 2 个函数:f1 和 f2
trait Test[+T] {
def f1[U >: T](a: U): Boolean
def f2[U](a: U): Boolean
}
它们是一样的吗?如果不是,有什么区别?
【问题讨论】:
-
嗯,您可以将任何类型的对象传递给这两个函数 - 请参阅 this question 了解说明。
以下 trait 声明了 2 个函数:f1 和 f2
trait Test[+T] {
def f1[U >: T](a: U): Boolean
def f2[U](a: U): Boolean
}
它们是一样的吗?如果不是,有什么区别?
【问题讨论】:
我认为它们在行为方面是相同的。这两个功能都需要为任何类型实现。但是类型同样可以为编译器提供更多信息以帮助您防止错误等,我可以想到一些奇怪的极端情况,其中一种可能比另一种更可取。例如:
class Foo
class Bar extends Foo
object BarChecker extends Test[Foo] {
def f1[U >: Foo](u: U): Boolean = {
if (u.isInstanceOf[Bar]) {
true
} else {
throw new RuntimeException("fail!")
}
}
def f2[U](u: U): Boolean = {
if (u.isInstanceOf[Bar]) {
true
} else {
throw new RuntimeException("fail!")
}
}
}
val x = BarChecker.f1[Foo](_) // can't do BarChecker.f1[Bar](_)
x(new Bar) // true
x(new Foo) // runtime exception
val y = BarChecker.f2[Bar](_)
y(new Bar) // true
y(new Foo) // compile error
当然,您可以通过以下方式解决此问题:
val x: Bar => Boolean = BarChecker.f1[Foo](_)
但我的观点是,不同的类型签名可能会对使用它们时可能或可能发生的错误产生不同的影响。
【讨论】:
T.f1[Foo](_) 中的T 是什么?
在执行方面,是的,它们是相同的。两者都接受 U 类型的参数,并返回布尔类型的结果。但是,就类型参数而言,它们不是。方法 f1 有一个 lower bound 类型参数,但方法 f2 没有。
那么,这是什么意思?
对于方法f2,您可以提供任何类型参数。在方法f1 的情况下,您只能提供等于T 类型或superType 的T 的类型参数。例如:
class Foo1(name:String)
class Foo2(name:String) extends Foo1(name)
class Foo3(name:String) extends Foo2(name)
class Test[+T] {
def f1[U >: T](a: U): Boolean = true
def f2[U](a: U): Boolean = true
}
val obj: Test[Foo2] = new Test[Foo2]
val foo1: Foo1 = new Foo1("1")
val foo2: Foo2 = new Foo2("2")
val foo3: Foo3 = new Foo3("3")
//F2 execute on any type you provide.
testInstance.f2[Foo1](foo1)
testInstance.f2[Foo2](foo2)
testInstance.f2[Foo3](foo3)
testInstance.f1[Foo2](foo2) //Foo2 is equal to type T.
testInstance.f1[Foo1](foo1) //Foo1 is supertype of T - Foo2.
testInstance.f1[Foo3](foo3) //Does not compile, because Foo3 is not superType of T - Foo2.
事实上,在Scala中,在Co-variance注解[+T]的情况下,你必须定义一个下界。以下将失败:
class Test[+T] {
def f1(a: T): Boolean = true //This will fail.
def f2[U](a: U): Boolean = true
}
【讨论】: