【Scala之旅】类型参数
泛型类泛型类将类的类型作为参数。他们作为集合类的时候尤其有用。 定义一个泛型类泛型类将类型作为参数放进中括号 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 } } 这个 使用通过将类型放入中括号中代替 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) 类 注意:泛型类型的子类型是不变的。这意味着如果我们有一堆类型为 型变型变是复杂类型的子类型关系以及它们的组件类型的子类型关系之间的相关性。Scala支持在泛型类的类型参数中使用型变注解;如果没有使用注解,则允许它们是协变的、逆变的或不变的。在类型系统中使用型变允许我们在复杂类型之间建立直观的联系,而缺乏形变可以限制类抽象的重用。 class Foo[+A] // A covariant class class Bar[-A] // A contravariant class class Baz[A] // An invariant class 协变一个泛型类的类型参数 个人注:这里的意思是,原先虽然 考虑一下这个简单的类结构: abstract class Animal { def name: String } case class Cat(name: String) extends Animal case class Dog(name: String) extends 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 } 逆变一个泛型类的类型参数 考虑 abstract class Printer[-A] { def print(value: A): Unit }
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) } 如果 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) } 这个程序的输出将会是: The cat's name is: Boots The animal's name is: Boots 不变泛型类在Scala中默认是不变的。这意味着,它们既不是协变和逆变。在接下来的例子中, class Container[A](value: A) { private var _value: A = value def getValue: A = _value def setValue(value: A): Unit = { _value = value } } 似乎一个 val catContainer: Container[Cat] = new Container(Cat("Felix")) val animalContainer: Container[Animal] = catContainer animalContainer.setValue(Dog("Spot")) val cat: Cat = catContainer.getValue // Oops,we'd end up with a Dog assigned to a Cat 幸运的是,编译器在很久之前就阻止了我们。 其他例子另一个可以帮助理解型变的例子是Scala标准库的 假设类似 class SmallAnimal class Mouse extends SmallAnimal 假设我们的函数接受 与其他语言相比一些类似于Scala的语言以不同的方式支持型变。在Scala中型变的注解相似于c#,它在定义类抽象时添加注解(声明位置变量)。但是,在Java中,当使用类抽象时(客户端使用型变),客户端会给出型变注解。 类型上界在 Scala 中,类型参数和抽象类型可能受到类型边界的约束。这种类型边界限制了类型变量的具体值,并且可能揭示了关于这些类型成员的更多信息。类型上边界 abstract class Animal { def name: String } abstract class Pet extends Animal {} class Cat extends Pet { override def name: String = "Cat" } class Dog extends Pet { override def name: String = "Dog" } class Lion extends Animal { override def name: String = "Lion" } class PetContainer[P <: Pet](p: P) { def pet: P = p } val dogContainer = new PetContainer[Dog](new Dog) val catContainer = new PetContainer[Cat](new Cat) // val lionContainer = new PetContainer[Lion](new Lion) // ^this would not compile
type arguments [Lion] do not conform to class PetContainer's type parameter bounds [P <: Pet] 这是因为 类型下界类型上界限制了一个类型为另一种类型的子类型,而类型下界则声明一个类型为另一种类型的超类型。 这里是一个非常有用的例子: trait Node[+B] { def prepend(elem: B): Unit } case class ListNode[+B](h: B,t: Node[B]) extends Node[B] { def prepend(elem: B) = ListNode[B](elem,this) def head: B = h def tail = t } case class Nil[+B]() extends Node[B] { def prepend(elem: B) = ListNode[B](elem,this) } 这个程序实现了一个单链表。 然而这个程序并不能编译,因为 为了解决这个问题,我们需要翻转 trait Node[+B] { def prepend[U >: B](elem: U) } case class ListNode[+B](h: B,t: Node[B]) extends Node[B] { def prepend[U >: B](elem: U) = ListNode[U](elem,this) def head: B = h def tail = t } case class Nil[+B]() extends Node[B] { def prepend[U >: B](elem: U) = ListNode[U](elem,this) } 现在我们可以做到以下几点: trait Bird case class AfricanSwallow() extends Bird case class EuropeanSwallow() extends Bird val africanSwallowList= ListNode[AfricanSwallow](AfricanSwallow(),Nil()) val birdList: Node[Bird] = africanSwallowList birdList.prepend(new EuropeanSwallow)
多态方法Scala中的方法可以通过类型和值进行参数化。语法类似于泛型类。类型参数用方括号括起来,而值参数用圆括号括起来。 下面是一个例子: def listOfDuplicates[A](x: A,length: Int): List[A] = { if (length < 1) Nil else x :: listOfDuplicates(x,length - 1) } println(listOfDuplicates[Int](3,4)) // List(3,3,3) println(listOfDuplicates("La",8)) // List(La,La,La)
在第一个示例调用中,我们通过写入 第二个示例调用显示你并不总是需要显式提供类型参数。 编译器通常可以根据上下文或值参数的类型来推断它。 在这个例子中, (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |