3

今天我花了几个小时来了解 Scala 中的下界背后的逻辑,但我读的越多,它就越混乱。你能解释一下吗?

这是我们研讨会的简单类层次结构:

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 根本不对类型参数施加任何限制。是否有隐含的 Bound toAnyAnyRef某处我错过了?

4

3 回答 3

3

让我解释一下有界类型推断的意外行为

  1. Upper Bound(T <: Pet):这意味着 T 适用于至少继承了 Pet 类或 Pet 的任何子类的所有类。
  2. Lower Bound(T >: Pet):这意味着 T 适用于所有继承了 Pet 类的至少一个父类的所有类

所以你猜对了,AnyRef 是所有对象/引用类型的超类型。所以当我们说

lowerBound(new Dog())

Dog 属于 AnyRef 类。所以通过下限,由于 AnyRef 是 Pet 的父级,编译器不会抛出任何警告。

您可以看到scala List 类的::方法的类似行为。使用 List,您可以执行以下操作而不会出现任何编译错误。

val list = List(1, 2, 3)

val newList = "Hello" :: list

如需进一步阅读,请查看这些堆栈溢出答案:

  1. https://stackoverflow.com/a/19217523/4046067
  2. https://stackoverflow.com/a/19821995/4046067
于 2017-03-19T12:28:09.820 回答
1

让我们看看为什么其中一些有效。-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. 对于最后一个,编译器推断AnyScala 类型层次结构中最高的,因为Any >: Pet.

所有这些都起作用,因为下限的定义是任何在类型层次结构中较高的类型都可能是候选类型。这就是为什么,如果我们举一个最人为的例子,传递一个作品,因为和Int的唯一共同祖先是。IntPetAny

于 2017-03-19T12:28:06.037 回答
0

您可以通过将定义更改为

def lowerBound[T](t: T)(implicit ev: Pet <:< T) = ...

这告诉编译器推断(在最后三种情况下T将是LionInt和),然后检查是否是 的子类型,而不是推断这样的子类型。Nil.typePetTT Pet

于 2017-03-20T07:20:05.743 回答