1.泛型和类型边界
1.1.上限(upper bounds
)
我们先看一个简单的实例,用于判断两个变量中较大的值,其中两个变量的类型均为Int型
package com.tv189.advanced /** * Created by molyeo on 2015/8/12. */ class PairInt(val first: Int, val second: Int) { def bigger = { if (first.compareTo(second) > 0) first else second } } object BoundTest { def main(args: Array[String]) { val pairInt = new PairInt(1, 2) println(pairInt.bigger) } }
上述PairInt类中的bigger方法调用了compare方法,如果我们想比较两个String型的变量的大小,我们可以和上面一样,添加PairStr类
class PairStr(val first: String, val second: String) { def bigger = { if (first.compareTo(second) > 0) first else second } }
如果我们针对每种基本类型都写一个具体的类,则代码量太大,同时也不够简洁,此时我们想到泛型能比较容易解决这个问题。
class Pair[T](val first: T, val second: T) { def bigger = { // if (first.compareTo(second) > 0) first else second } }
然而与此同时,我们定义的泛型T并指定没有实现compareTo方法,也没有指定为某个类型的子类。在Java泛型里表示某个类型是Test类型的子类型,使用extends关键字:
<T extends Test> //或用通配符的形式: <? extends Test>
这种形式也叫upper bounds
(上限或上界),同样的意思在Scala中的写法为
[T <: Test] //或用通配符: [_ <: Test]
上面的需求采用泛型结合上限,代码如下:
package com.tv189.advanced /** * Created by molyeo on 2015/8/12. */ class Pair[T <: Comparable[T]](val first: T, val second: T) { def bigger = { if (first.compareTo(second) > 0) first else second } } object BoundTest { def main(args: Array[String]) { val pairStr = new Pair[String]("hadoop", "spark") println(pairStr.bigger) val pairInt = new Pair[Integer](1, 2) println(pairInt.bigger) } }
1.2.下限(lower bounds
)
与上限相对应,在Scala中也有lower bounds
(下限或下界),即表示某个类型是Test类型的父类型
[T >: Test] //或用通配符: [_ >: Test]
下限具体有什么用,主要是用于类型的安全转型,先看下面的代码
class Pair_Lower[T](val first: T, val second: T) { def replaceFirst[R >: T](newFirst: R): Pair_Lower[R] = new Pair_Lower[R](newFirst, second) }
我们创建Pair_Lower的类,其中可以包含两个元素,元素类型为泛型的T。
Pair_Lower类中有一个replaceFirst方法,用来把第二个元素和一个新的元素结合起来组成一个新的Pair_Lower。新的元素的类型是泛型的R。新组成的Pair_Lowe的类型是Pair_Lower[R]。
新的Pair_Lower的类型怎么能是Pair_Lower[R]呢?replaceFirst的签名给我们说明了这一点。[R >: T]。这种标记的含义是说R是T的基类。那么一个T和一个R自然可以组合成一个R的Pair_Lower。接下来我们看一个下限的具体例子
package com.tv189.advanced /** * Created by molyeo on 2015/8/12. */ class Pair_Upper[T <: Comparable[T]](val first: T, val second: T) { def bigger = { if (first.compareTo(second) > 0) first else second } } class Pair_Lower[T](val first: T, val second: T) { def replaceFirst[R >: T](newFirst: R): Pair_Lower[R] = new Pair_Lower[R](newFirst, second) } class Vehicle {} class Car extends Vehicle {} class Tank extends Vehicle {} object BoundTest { def main(args: Array[String]) { val twoCars: Pair_Lower[Car] = new Pair_Lower(new Car(), new Car()) val tankAndCar: Pair_Lower[Vehicle] = twoCars.replaceFirst(new Tank()) } }
首先,我们用两辆汽车组成一个twoCars(Pair_Lower[Car]),然后用一个tank替代原来twoCars中的第一个元素,形成新的Pair_Lower[Vehicle]。
总结一下就是如果newFirst的类型刚好是T的基类,那么R就直接是newFirst的类型。如果newFirst的类型不是T的基类,那R就会是T和newFirst的类型的共同基类。
2.视图边界(View Bounds)
上面上限的代码存在的问题在于对于Int类型的Pair,我们需要指定Pair的类型为[Integer]
val pairInt = new Pair[Integer](1, 2) println(pairInt.bigger)
否则我们直接用代码
val pairInt = new Pair(1, 2) println(pairInt.bigger)
则运行的时候会报错
Error:(40, 19) inferred type arguments [Int] do not conform to class Pair‘s type parameter bounds [T <: Comparable[T]] val pairInt = new Pair(1, 2)
原因在于Scala类型中的Int类型不属于Comparable[T]边界,然而java中的Integer对象属于Comparable[T]边界。
如果我们不想指定Integer类型,则可以考虑使用Scala的视图边界,写法为
[T <% Test]
代码完善为
//View Bounds class Pair_Upper_Better[T <% Comparable[T]](val first: T, val second: T) { def bigger = { if (first.compareTo(second) > 0) first else second } }
我们则可以直接调用如下代码构建Pair_Upper_Better进行比较
val pairStrBetter = new Pair_Upper_Better("hadoop", "spark") println(pairStrBetter.bigger) val pairIntBetter = new Pair_Upper_Better(1, 2) println(pairIntBetter.bigger)
原理是因为在Scala内部发生了隐式转换,将Int类型转换为RichInt类型。
此外如果我们想对String类型的Pair也采用操作符进行比较,则可以采用Ordered[T]来完善代码,具体见Pair_Upper_Perfect类,使代码看起来更加简洁明了。
package com.tv189.advanced /** * Created by molyeo on 2015/8/12. */ class Pair_Upper[T <: Comparable[T]](val first: T, val second: T) { def bigger = { if (first.compareTo(second) > 0) first else second } } //View Bounds class Pair_Upper_Better[T <% Comparable[T]](val first: T, val second: T) { def bigger = { if (first.compareTo(second) > 0) first else second } } class Pair_Upper_Perfect[T <% Ordered[T]](val first: T, val second: T) { def bigger = { if (first>second) first else second } } //class Pair_Lower[T](val first: T, val second: T) { // def replaceFirst[R >: T](newFirst: R): Pair_Lower[R] = new Pair_Lower[R](newFirst, second) //} //class Vehicle {} // //class Car extends Vehicle {} // //class Tank extends Vehicle {} object BoundTest { def main(args: Array[String]) { // val pairStr = new Pair_Upper[String]("hadoop", "spark") // println(pairStr.bigger) // // val pairInt = new Pair_Upper[Integer](1, 2) // println(pairInt.bigger) // val twoCars: Pair_Lower[Car] = new Pair_Lower(new Car(), new Car()) // val tankAndCar: Pair_Lower[Vehicle] = twoCars.replaceFirst(new Tank()) // val pairStrBetter = new Pair_Upper_Better("hadoop", "spark") // println(pairStrBetter.bigger) // // val pairIntBetter = new Pair_Upper_Better(1, 2) // println(pairIntBetter.bigger) val pairStrPerfect = new Pair_Upper_Perfect("hadoop", "spark") println(pairStrPerfect.bigger) val pairIntPerfect = new Pair_Upper_Perfect(1, 2) println(pairIntPerfect.bigger) } }