文章目录
Scala是静态类型的,它拥有一个强大的类型系统,静态地强制以安全、一致的方式使用抽象,我们通过下面几个特征来一一说明:
- 泛类型
- 型变
- 类型上界
- 类型下界
- 内部类
- 抽象类型
- 复合类型
- 自类型
- 隐式参数
- 隐式转换
- 多态方法
- 类型推断
通过这些特性,为安全可重用的编程抽象以及类型安全的扩展提供了强大的基础。
泛类型
和java一样,Scala也有泛型的概念,在scala里面泛型是使用方括号 [] 来接受类型参数的。通常使用字母A来作为参数标志符,当然你也可以使用其他任意的参数名称。
class Stack[A] { private var elements: List[A] = Nil def push(x: A) { elements = x :: elements } def peek: A = elements.head def pop(): A = { val currentTop = peek elements = elements.tail currentTop } }
要使用一个泛类型,将一个具体的类型替换掉A即可。
val stack = new Stack[Int] stack.push(1) stack.push(2) println(stack.pop) // prints 2 println(stack.pop) // prints 1
上面的例子中,实例对象接收整型值,如果该类型有子类型,子类型也是可以传入的。
class Fruit class Apple extends Fruit class Banana extends Fruit val stack = new Stack[Fruit] val apple = new Apple val banana = new Banana stack.push(apple) stack.push(banana)
型变
型变主要是针对泛类型来说的,用来表示这种复杂类型的相关性。型变主要有协变,逆变和不变三种情况。在类型系统中使用型变允许我们在复杂类型之间建立直观的连接,而缺乏型变则会限制类抽象的重用性。
class Foo[+A] // A covariant class class Bar[-A] // A contravariant class class Baz[A] // An invariant class
协变
协变使用+A来表示。对于某些类 class List[+A],使 A 成为协变意味着对于两种类型 C 和 D,如果 C 是 D 的子类型,那么 List[C] 就是 List[D] 的子类型。 这允许我们使用泛型来创建非常有用和直观的子类型关系。
abstract class Animal { def name: String } case class Cat(name: String) extends Animal case class Dog(name: String) extends Animal
上面的例子中类型 Cat 和 Dog 都是 Animal 的子类型。那么
List[Cat]和List[Dog]都是List[Animal]的子类。
下面看下协变的应用:
object CovarianceTest extends App { def printAnimalNames(animals: List[Animal]): Unit = { animals.foreach { animal => println(animal.name) } } val cats: List[Cat] = List(Cat("Whiskers"), Cat("Tom")) val dogs: List[Dog] = List(Dog("Fido"), Dog("Rex")) printAnimalNames(cats) // Whiskers // Tom printAnimalNames(dogs) // Fido // Rex }
在上面的例子中,方法 printAnimalNames 将接受动物列表作为参数,并且逐行打印出它们的名称。 如果 List[A] 不是协变的,最后两个方法调用将不能编译,这将严重限制 printAnimalNames 方法的适用性。
逆变
逆变和协变相反,使用-A来表示。对于某个类 class Writer[-A] ,使 A 逆变意味着对于两种类型 C 和 D,如果 C 是 D 的子类型,那么 Writer[D] 是 Writer[C] 的子类型。
考虑一下的例子:
abstract class Printer[-A] { def print(value: A): Unit }
我们定义两个printer:
class AnimalPrinter extends Printer[Animal] { def print(animal: Animal): Unit = println("The animal's name is: " + animal.name) } class CatPrinter extends Printer[Cat] { def print(cat: Cat): Unit = println("The cat's name is: " + cat.name) }
如果 Printer[Cat] 知道如何在控制台打印出任意 Cat,并且 Printer[Animal] 知道如何在控制台打印出任意 Animal,那么 Printer[Animal] 也应该知道如何打印出 Cat 就是合理的。 反向关系不适用,因为 Printer[Cat] 并不知道如何在控制台打印出任意 Animal。 因此,如果我们愿意,我们应该能够用 Printer[Animal] 替换 Printer[Cat],而使 Printer[A] 逆变允许我们做到这一点。
object ContravarianceTest extends App { val myCat: Cat = Cat("Boots") def printMyCat(printer: Printer[Cat]): Unit = { printer.print(myCat) } val catPrinter: Printer[Cat] = new CatPrinter val animalPrinter: Printer[Animal] = new AnimalPrinter printMyCat(catPrinter) printMyCat(animalPrinter) }
不变
默认情况下,Scala中的泛型类是不变的。这意味着虽然Cat是Animal的子类,但是Container[Cat]和Container[Animal]之间没有任何关系。