List[T] forSome {type T}
和List[T forSome {type T}]
有什么区别?如何用英语阅读它们?我应该如何理解forSome
关键字?forSome
的一些实际用途是什么?除了简单的T forSome {type T}
用法外,还有哪些有用的实用且更复杂的用法?
List[T] forSome {type T}
和List[T forSome {type T}]
有什么区别?如何用英语阅读它们?我应该如何理解forSome
关键字?forSome
的一些实际用途是什么?除了简单的T forSome {type T}
用法外,还有哪些有用的实用且更复杂的用法?
注意:(更新于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将为您提供一个未知类型T
的List
。它将很乐意接受这个列表,并与之一起工作。但它不会告诉您T
是什么。但您至少知道,列表中的所有元素都是相同类型的T
。
第二个稍微有点棘手。再次地,API将向您提供一个List
。它将使用一些类型T
,而不告诉您T
是什么。但它可以为每个元素选择不同的类型。真实的API会为T
建立一些约束条件,以便它可以实际上使用列表的元素。
forSome
很有用。每个实现将为您提供一些对象,并接受这些对象返回。但您既不能混合来自不同实现的对象,也不能自己创建对象。相反,您必须始终使用相应的API函数获取一些可与该API一起使用的对象。forSome
启用了一种非常严格的封装方式。您可以这样理解forSome
:
API合同适用于某些类型。但是您不知道适用于哪些类型。因此,您无法提供自己的类型,也无法创建自己的对象。您必须使用使用
forSome
的API提供的对象。
这种解释比较口语化,可能在某些角落情况下甚至是错误的。但它应该能帮助您理解这个概念。
这里有很多问题,其中大部分都已经在评论中的答案中得到了充分的解答,所以我将回答你更具体的第一个问题。
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
的foo列表”。整个列表只有一个T
。另一方面,第二个可以解释为“foo列表,其中每个foo都是某种T
的”。outer: Outer
,我们可以说“存在某种类型T
,使得outer
是T
类型的foo列表”,而对于类型Inner
的列表,我们只能说“对于列表的每个元素,都存在某个T
,使得该元素是T
类型的foo”。后者更弱——它告诉我们关于列表的信息更少。val inner: Inner = List(new Foo[Char], new Foo[Int])
val outer: Outer = List(new Foo[Char], new Foo[Int])
第一个代码可以成功编译,因为列表中的每个元素都是Foo[T]
类型的,其中T是某种类型。第二个代码无法编译,因为没有一种类型T能够使得列表中的每个元素都是Foo[T]
类型的。
forSome {type X}
替换为 forSome {object x}
。 - ayvango