Effictive Scala(2)
变型
变型(Variance)发生在发型与子类型化(subtyping)结合的时候。与容器类型的子类型化有关,它们定义了对所包含的类型如何子类型化。因为scala有声明点变型(declaration variance)注释,公共库的作者–特别是集合–必须有丰富的注释器。这些注释对共享代码的可用性很重要,但滥用也会很危险。
不可变(invariants)是scala类型系统中高级部分,但也是必须的一面,应该使用广泛的(并且正确的),它有助于子类型化的应用。
不可变集合应该是斜变的。方法接受的类型能够应该适当的降级。
trait Collection[T] {
def add[U >: T](other: U): Collection[U]
}
可变(mutable)集合应该是不可变的(invariant)。斜变对于可变集合是无效的。
trait HashSet[+T] {
def add[U >: T] (item: U)
}
下面的类型层级:
trait Mammal
trait Dog extends Mammal
trait Cat extends Mammal
如果我现在有一个狗的HashSet:
val dogs: HashSet[Dog]
当它为一个哺乳动物的Set,增加一只猫(cat)
val mamals: HashSet[Mammal] =dogs
mammals.add(new Cat{})
这将不再是一个只存储狗的HashSet
类型别名
使用类型别名。 当他们提供了便捷的命名或阐明意图时,但对于自解释类型不要使用类型别名。
比如:
() => Int
比以下定义的别名IntMaker更清晰
type IntMaker = () => Int
IntMaker
但,下面的别名:
class ConcurrentPool[K, V]{
type Queue = ConcurrentLinkedQueue[V]
type Map = ConcurrentHashMap[K, Queue]
..
}
有助于交流的目的并使得更加简短
当使用类型别名的时候不要使用子类型化(subtyping)
trait SocketFactory extends (SocketAddress => Socket)
SocketFactory 是一个生产Socket的方法。使用一个类型别名更好:
type SocketFactory = SocketAddress => Socket
我们现在可以对SocketFactory类型的值,提供函数字面量(function literals),也可以使用函数组合:
val addrToInet: SocketAddress => Long
val inetToSocket: Long => Socket
val factory: SocketFactory = addrToInet andThen inentToSocket
类型别名通过用package object 将名字绑定在顶层:
package com.twitter
package object net {
type SocketFactory = (SocketAddress) => Socket
}
注意类型别名不是新类型--他们等价于在语法上的用别名代替了原类型。
隐式转换
隐式转换是类型系统里一个强大的功能,但应当谨慎的使用。它们有复杂的解决规则为难你,通过简单的语法检查,领会实际发生什么。在下面的场景使用隐式转换:
1.扩展或增加一个scala风格的集合
2.适配或扩展一个对象
3.通过提供约束证据来加强类型安全。
4.To provided type evidence
5.For Manifests
如果你发现自己在用隐式转换,总要问问自己是否不使用这种方式也可以达到目的。
不要使用隐式转换对来年各个相似的数据类型做自动转换(例如,把list转换为stream)。