List[T] forSome {type T}
和List[T forSome {type T}]
之间有什么区别?我如何用“英语”阅读它们?我应该如何 grok forSome
关键字? forSome
有哪些实际用途?什么是实用的,比简单的T forSome {type T}
用法更复杂?
答案 0 :(得分:25)
这里有很多问题,其中大部分已经在上述评论中链接的答案中得到了相当深入的解答,所以我会回答你更具体的第一个问题。
List[T] forSome { type T }
和List[T forSome { type T }]
之间没有真正有意义的区别,但我们可以看到以下两种类型之间的差异:
class Foo[A]
type Outer = List[Foo[T]] forSome { type T }
type Inner = List[Foo[T] forSome { type T }]
我们可以将第一个读作“T
的foos列表,对于某些类型T
”。整个列表中只有一个T
。另一方面,第二个可以被理解为“一个foos列表,其中每个foo对于某些T
来说都是T
。”
换句话说,如果我们有一个列表outer: Outer
,我们可以说“存在某种类型T
,outer
是T
的列表{1}}“,对于类型Inner
的列表,我们只能说”对于列表的每个元素,存在一些T
,使得该元素是{{1}的foo }}”。后者较弱 - 它告诉我们较少的清单。
因此,例如,如果我们有以下两个列表:
T
第一个将编译得很好 - 列表的每个元素对于某些val inner: Inner = List(new Foo[Char], new Foo[Int])
val outer: Outer = List(new Foo[Char], new Foo[Int])
是Foo[T]
。第二个不会编译,因为没有一些T
,因此列表的每个元素都是T
。
答案 1 :(得分:21)
注意:(更新2016-12-08 )根据Martin Odersky关于ScalaX 2016的演讲,forSome关键字很可能会消失Scala 2.13或2.14将其替换为路径依赖类型或匿名类型属性(A[_]
)。在大多数情况下这是可能的。如果您的边缘情况不可能,请重构您的代码或放宽您的类型限制。
通常当您使用通用API时,API会保证您可以使用您提供的任何类型(最多一些给定的约束)。因此,当您使用List[T]
时,List API会向您保证它可以与您提供的任何类型T
一起使用。
使用forSome
(所谓的存在量化类型参数),反之亦然。 API 将提供一种类型(不是你)并且它保证你,它将使用它为你提供的这种类型。语义是,具体对象将为您提供T
类型的东西。同一个对象也会接受它为您提供的内容。但是没有其他对象可以使用这些T
,并且没有其他对象可以为您提供T
类型的内容。
“存在量化”的概念是:存在(至少)一种类型T
(在实现中)以履行API的合同。但我不会告诉你它是哪种类型。
forSome
可以类似地读取:对于某些类型T
,API合同适用。但对于所有类型T
都不一定如此。因此,当您提供某种类型T
(而不是API实现中隐藏的类型)时,编译器无法保证您获得了正确的T
。所以它会抛出一个类型错误。
因此,当您在API中看到List[T] forSome {type T}
时,您可以这样阅读:API将为您提供某种未知类型List
的{{1}}。它很乐意接受这个列表,它将与它一起使用。但它不会告诉你,T
是什么。但至少你知道,列表中的所有元素都属于同一类型T
。
第二个有点棘手。 API再次为您提供T
。并且它将使用某种类型List
而不会告诉您T
是什么。但是可以自由地为每个元素选择不同的类型。真实世界的API会为T
建立一些约束,因此它实际上可以使用列表的元素。
T
非常有用,其中每个对象代表API的实现。每个实现都将为您提供一些对象,并将接受这些对象。但是你既不能混合不同实现的对象,也不能自己创建对象。相反,您必须始终使用相应的API函数来获取可与该API一起使用的一些对象。 forSome
支持非常严格的封装。您可以通过以下方式阅读forSome
:
对于某些类型,API合约折叠为true。但你不知道 哪种类型适用。因此,您无法提供自己的类型和 你不能创建自己的对象。你必须使用提供的 通过使用
的APIforSome
。
这是非常非正式的,在某些极端情况下甚至可能是错误的。但它应该帮助你理解这个概念。