Scala 下界的行为与我预期的不一样
Scala lower bound does not behave as I expect
今天我花了几个小时来理解 Scala 中 Lower Bounds 背后的逻辑,但我读得越多就越感到困惑。你能解释一下吗?
这是我们研讨会的简单 class 层次结构:
class Animal
class Pet extends Animal
class Wild extends Animal
class Dog extends Pet
class Cat extends Pet
class Lion extends Wild
class Tiger extends Wild
所以层次结构应该是这样的:
Animal
/ \
Pet Wild
/ \ / \
Dog Cat Lion Tiger
这是客户端代码:
object Main extends App {
//I expect the compilation of passing any type above Pet to fail
def upperBound[T <: Pet](t: T) = {println(t.getClass.getName)}
//I expect the compilation of passing any type below Pet to fail
def lowerBound[T >: Pet](t: T) = {println(t.getClass.getName)}
upperBound(new Dog)//Ok, As expected
upperBound(new Cat)//Ok, As expected
upperBound(new Pet)//Ok, As expected
//Won't compile (as expected) because Animal is not a sub-type of Pet
upperBound(new Animal)
lowerBound(new Pet)//Ok, As expected
lowerBound(new Animal)//Ok, As expected
//I expected this to fail because Dog is not a super type of Pet
lowerBound(new Dog)
//I expected this to fail because Lion is not a super type of Pet either
lowerBound(new Lion)
lowerBound(100)//Jesus! What's happening here?!
lowerBound(Nil)// Ok! I am out!!! :O
}
嗯...代码的最后四行对我来说没有任何意义!据我了解, Lower Bound 根本不对类型参数施加任何约束。是否有隐式绑定到 Any
或 AnyRef
我遗漏的地方?
让我们看看为什么其中一些有效。您可以使用 -Xprint:typer
标志检查 scalac 推断的类型。这是我们看到的:
NewTest.this.lowerBound[tests.NewTest.Pet](new NewTest.this.Dog());
NewTest.this.lowerBound[tests.NewTest.Animal](new NewTest.this.Lion());
NewTest.this.lowerBound[Any](100)
对于 Dog
的情况,编译器查找将匹配下限要求的祖先,自 Pet >: Pet
以来 Pet
满足该要求。对于 Lion
,符合要求的祖先是 Animal
,自 Animal >: Pet
起有效。对于最后一个,编译器推断出 Any
,这是 Scala 类型层次结构中最高的,这也有效,因为 Any >: Pet
.
所有这些都有效,因为下限的定义是类型层次结构中较高的任何类型都可能是候选者。这就是为什么,如果我们举一个最人为的例子,传递一个 Int
是有效的,因为 Int
和 Pet
的唯一共同祖先是 Any
。
让我解释一下有界类型推断的意外行为
- Upper Bound(T <: Pet): 这意味着 T 适用于所有至少继承了 Pet class 或任何 Pet 的 subclass 的 classes es.
- Lower Bound(T >: Pet):这意味着 T 适用于所有继承了 Pet class 至少一个父 class 的所有 classes class.
正如您猜对的那样,AnyRef 是所有 object/reference 类型的超类型。所以当我们说
lowerBound(new Dog())
狗在 class AnyRef 下。所以根据下限,因为 AnyRef 是 Pet 的父级,编译器不会抛出警告。
您可以看到 scala List class 的 :: 方法的类似行为。使用 List,您可以在没有任何编译错误的情况下执行以下操作。
val list = List(1, 2, 3)
val newList = "Hello" :: list
如需进一步阅读,请查看这些堆栈溢出答案:
您可以通过将定义更改为
来获得预期的结果
def lowerBound[T](t: T)(implicit ev: Pet <:< T) = ...
这告诉编译器推断 T
(因此在后三种情况下它将是 Lion
、Int
和 Nil.type
)和 然后 检查 Pet
是否是 T
的子类型,而不是推断 T
这样 Pet
是一个亚型。
今天我花了几个小时来理解 Scala 中 Lower Bounds 背后的逻辑,但我读得越多就越感到困惑。你能解释一下吗?
这是我们研讨会的简单 class 层次结构:
class Animal
class Pet extends Animal
class Wild extends Animal
class Dog extends Pet
class Cat extends Pet
class Lion extends Wild
class Tiger extends Wild
所以层次结构应该是这样的:
Animal
/ \
Pet Wild
/ \ / \
Dog Cat Lion Tiger
这是客户端代码:
object Main extends App {
//I expect the compilation of passing any type above Pet to fail
def upperBound[T <: Pet](t: T) = {println(t.getClass.getName)}
//I expect the compilation of passing any type below Pet to fail
def lowerBound[T >: Pet](t: T) = {println(t.getClass.getName)}
upperBound(new Dog)//Ok, As expected
upperBound(new Cat)//Ok, As expected
upperBound(new Pet)//Ok, As expected
//Won't compile (as expected) because Animal is not a sub-type of Pet
upperBound(new Animal)
lowerBound(new Pet)//Ok, As expected
lowerBound(new Animal)//Ok, As expected
//I expected this to fail because Dog is not a super type of Pet
lowerBound(new Dog)
//I expected this to fail because Lion is not a super type of Pet either
lowerBound(new Lion)
lowerBound(100)//Jesus! What's happening here?!
lowerBound(Nil)// Ok! I am out!!! :O
}
嗯...代码的最后四行对我来说没有任何意义!据我了解, Lower Bound 根本不对类型参数施加任何约束。是否有隐式绑定到 Any
或 AnyRef
我遗漏的地方?
让我们看看为什么其中一些有效。您可以使用 -Xprint:typer
标志检查 scalac 推断的类型。这是我们看到的:
NewTest.this.lowerBound[tests.NewTest.Pet](new NewTest.this.Dog());
NewTest.this.lowerBound[tests.NewTest.Animal](new NewTest.this.Lion());
NewTest.this.lowerBound[Any](100)
对于 Dog
的情况,编译器查找将匹配下限要求的祖先,自 Pet >: Pet
以来 Pet
满足该要求。对于 Lion
,符合要求的祖先是 Animal
,自 Animal >: Pet
起有效。对于最后一个,编译器推断出 Any
,这是 Scala 类型层次结构中最高的,这也有效,因为 Any >: Pet
.
所有这些都有效,因为下限的定义是类型层次结构中较高的任何类型都可能是候选者。这就是为什么,如果我们举一个最人为的例子,传递一个 Int
是有效的,因为 Int
和 Pet
的唯一共同祖先是 Any
。
让我解释一下有界类型推断的意外行为
- Upper Bound(T <: Pet): 这意味着 T 适用于所有至少继承了 Pet class 或任何 Pet 的 subclass 的 classes es.
- Lower Bound(T >: Pet):这意味着 T 适用于所有继承了 Pet class 至少一个父 class 的所有 classes class.
正如您猜对的那样,AnyRef 是所有 object/reference 类型的超类型。所以当我们说
lowerBound(new Dog())
狗在 class AnyRef 下。所以根据下限,因为 AnyRef 是 Pet 的父级,编译器不会抛出警告。
您可以看到 scala List class 的 :: 方法的类似行为。使用 List,您可以在没有任何编译错误的情况下执行以下操作。
val list = List(1, 2, 3)
val newList = "Hello" :: list
如需进一步阅读,请查看这些堆栈溢出答案:
您可以通过将定义更改为
来获得预期的结果def lowerBound[T](t: T)(implicit ev: Pet <:< T) = ...
这告诉编译器推断 T
(因此在后三种情况下它将是 Lion
、Int
和 Nil.type
)和 然后 检查 Pet
是否是 T
的子类型,而不是推断 T
这样 Pet
是一个亚型。