###翻译: #交叉类型
trait Resettable { def reset(): this.type}trait Growable[T] { def add(x: T): this.type}def f(x: Resettable & Growable[String]) = { x.reset() x.add("first")}
这里x要求必须是Resettable
类型和Growable[T]
类型,交叉类型A & B
替代了scala2中的复合类型A with B
(目前,这种复合类型仍然可以使用,但是未来会被废除)
与 with
复合类型不同,&
交叉类型是可互换的,比如:A & B
和B & A
是相同的类型。 一个交叉类型A & B
的所有成员,是类型A
的所有成员和类型B
的所有成员之和。
例如Resettable & Growable[String]
类型拥有reset
和add
两个分别来自两个类型的成员方法。
如果一个成员既出现在A
类型中,又出现在B
类型中,那么这个成员的类型在类型A & B
中表现为A
类型和B
类型交叉。比如下面的例子:
trait A { def children: List[A] def say:Int = 1}trait B { def children: List[B] def say:String = "2"}class C extends A with B { def children: List[A & B] = List(new C) override def say:Int & String = super.say + 3 }val x: A & B = new Cval xs: List[A] & List[B] = x.childrenval ys: List[A & B] = x.children
这个成员children
在 A & B
的类型,是 children
在A
中的类型List[A]
和children
在B
中类型List[B]
的交叉类型List[A] & List[B]
。这个能进一步简化为List[A & B]
,因为List
是协变的。
联合类型
翻译:
操作符|
创建一个联合类型
case class UserName(name: String) { def lookup(admin: Admin): UserData}case class Password(hash: Hash) { def lookup(admin: Admin): UserData}def help(id: UserName | PassWord) = { val user = id match { case UserName(name) => lookupName(name) case Password(hash) => lookupPassword(hash) } // ...}
联合类型是双重交叉类型。联合类型A|B
的值是所有A
的值和所有B
的值之和。 |
联合类型是可互换的,比如:A | B
和B | A
是相同的类型。
只有当一个表达式明确给出类型的时候,编译器才会给这个表达式赋值一个联合类型,例如:
scala> val password = Password(123)val password: Password = Password(123)scala> val name = UserName("Eve")val name: UserName = UserName(Eve)scala> if (true) name else passwordval res2: Object & Product = UserName(Eve)scala> val either: Password | UserName = if (true) name else passwordval either: Password | UserName = UserName(Eve)scala>help(either)
res2
的类型是Object & Product
,它是 UserName
和 Product
的一个超类,但不是类型的最小超类Password | UserName
。如果想获得最小超类,必须明确给出类型定义,就像这里的val either: Password | UserName
的类型。要调用联合类型的成员,使用类型匹配match
,比如这里的help
方法
#文本单例类型 看以下例子:
object Literals{ val fortyTwo: 42 = 42 val `2`: 2 = 2 val fortyFour: 44 = fortyTwo + `2` val text: "text" = "text" def id[T](a: T) = a val two: 2 = id(`2`) final val T = 1val x: T.type = 2 // error , 2 should be 1}
这里 val x: T.type = 2
会报错,因为T.type
文本类型只能赋值为原本定义的值,这里是1
。这里的final val
定义个值不可变的单例文本类型,使用T.type
来引用,且只能赋值为原值。