好吧,我想我应该对此有自己的看法,而不是仅仅发表评论。抱歉,这会很长,如果您想要 TL;DR 跳到最后。
正如兰德尔·舒尔茨所说,这里_
是存在类型的简写。即,
class Foo[T <: List[_]]
是一个简写
class Foo[T <: List[Z] forSome { type Z }]
请注意,与 Randall Shulz 的答案提到的相反(完全披露:我在这篇文章的早期版本中也弄错了,感谢 Jesper Nordenberg 指出),这与以下内容不同:
class Foo[T <: List[Z]] forSome { type Z }
它也不等同于:
class Foo[T <: List[Z forSome { type Z }]]
请注意,很容易出错(正如我之前的错误所示):兰德尔·舒尔茨的答案引用的文章的作者自己也出错了(请参阅评论),并在稍后修复了它。我对本文的主要问题是,在所示的示例中,存在性的使用应该可以使我们避免打字问题,但事实并非如此。去检查代码,然后尝试编译compileAndRun(helloWorldVM("Test"))
or compileAndRun(intVM(42))
。是的,无法编译。简单制作compileAndRun
通用在A
将使代码编译,并且会简单得多。
简而言之,这可能不是了解存在主义及其优点的最佳文章(作者本人在评论中承认该文章“需要整理”)。
所以我更推荐阅读这篇文章:http://www.artima.com/scalazine/articles/scalas_type_system.html,特别是名为“存在类型”和“Java 和 Scala 中的差异”的部分。
您应该从本文中得到的重要一点是,在处理非协变类型时,存在性非常有用(除了能够处理通用 java 类之外)。
这是一个例子。
case class Greets[T]( private val name: T ) {
def hello() { println("Hello " + name) }
def getName: T = name
}
这个类是通用的(还要注意它是不变的),但我们可以看到hello
实际上没有使用任何类型参数(与getName
),所以如果我得到一个实例Greets
我应该总是能够调用它,无论如何T
是。如果我想定义一个方法Greets
实例并调用它的hello
方法,我可以尝试这个:
def sayHi1( g: Greets[T] ) { g.hello() } // Does not compile
果然,这不能编译,因为T
这里不知从何而来。
好吧,让我们使该方法通用:
def sayHi2[T]( g: Greets[T] ) { g.hello() }
sayHi2( Greets("John"))
sayHi2( Greets('Jack))
太好了,这有效。我们还可以在这里使用存在主义:
def sayHi3( g: Greets[_] ) { g.hello() }
sayHi3( Greets("John"))
sayHi3( Greets('Jack))
也有效。所以总而言之,使用存在主义并没有真正的好处(如sayHi3
)超过类型参数(如sayHi2
).
然而,如果Greets
它本身作为另一个泛型类的类型参数出现。举例来说,我们要存储多个实例Greets
(与不同的T
)在列表中。让我们尝试一下:
val greets1: Greets[String] = Greets("John")
val greets2: Greets[Symbol] = Greets('Jack)
val greetsList1: List[Greets[Any]] = List( greets1, greets2 ) // Does not compile
最后一行无法编译,因为Greets
是不变的,所以Greets[String]
and Greets[Symbol]
不能被视为Greets[Any]
虽然String
and Symbol
两者都延伸Any
.
好的,让我们尝试一下存在主义,使用简写符号_
:
val greetsList2: List[Greets[_]] = List( greets1, greets2 ) // Compiles fine, yeah
这编译得很好,你可以按照预期做:
greetsSet foreach (_.hello)
现在,请记住,我们首先遇到类型检查问题的原因是Greets
是不变的。如果它变成一个协变类(class Greets[+T]
)那么一切都会开箱即用,我们永远不需要存在主义。
总而言之,存在性对于处理泛型不变类很有用,但是如果泛型类不需要将自身显示为另一个泛型类的类型参数,那么您很可能不需要存在性,只需添加类型参数即可你的方法会起作用
现在回到你的具体问题(最后,我知道了!)
class Foo[T <: List[_]]
Because List
是协变的,这对于所有意图和目的来说都与刚才说的相同:
class Foo[T <: List[Any]]
所以在这种情况下,使用任何一种表示法实际上只是一个风格问题。
但是,如果您更换List
with Set
, 事情会改变的:
class Foo[T <: Set[_]]
Set
是不变的,因此我们处于与Greets
我的例子中的类。因此上面的内容确实与
class Foo[T <: Set[Any]]