[Scala基础系列 10]Scala泛型、类型边界

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)

  }
}
时间: 2024-10-08 09:09:22

[Scala基础系列 10]Scala泛型、类型边界的相关文章

[Scala基础系列 04]Scala基本类型

1.Scala的数值类型 Scala的数值类型与Java类似,他们的范围与Java也是一致的.与Java不同的是,他们都是对象,是相应的数值类的实例.Scala通过富包装(Rich Wrapper)类给这些数值类型提供了强大的支持. 1.1.数值类型 Scala的数值类型和取值范围,见下表. Boolean: true 或者 false Byte: 8位, 有符号(2-7 ~ 27 - 1) Short: 16位, 有符号 (2-15 ~ 215 - 1) Int: 32位, 有符号 (2-31

[Scala基础系列 05]Scala控制结构

1.If语句 Scala的If语句可以完成其他语言中If语句,于此同时,if/else通常还有值,可以用来赋值,或者代替三元条件运算符(?:).不过它可以比条件运算符更强大,因为你可以在if-else里面写很复杂的程序块. 1.1.普通的If语句 package com.tv189.foundation /** * Created by molyeo on 2015/7/30. */ object IfCondition { def main(args: Array[String]) { val

[Scala基础系列 07]Scala集合

Scala有一个非常通用,丰富,强大,可组合的集合库:集合是高阶的(high level)并暴露了一大套操作方法.很多集合的处理和转换可以被表达的简洁又可读,但不审慎地用它们的功能也会导致相反的结果.每个Scala程序员应该阅读 集合设计文档:通过它可以很好地洞察集合库,并了解设计动机. 1.数组(Array&ArrayBuffer) 1.1.Array 数组(Array)其实并不在scala.collection包里面,它属于scala包,直接对应于Java的数组,比如,Scala中的Arra

[Scala基础系列 03]Scala操作符

1.Scala操作符简介 首先,请记住,Scala没有操作符!也没有通常意义上的表达式.你所见到的类似操作符和表达式的语句,其实是方法(函数),它们只是方法的一种比较直观的写法,可以叫做操作符记法. 1.1.二元操作符(中缀表达式) 二元操作符是最常见的操作符,比如,一个简单的表达式1 + 2.其实,“+”是定义在Int类的一个方法,你完全可以用普通方法调用的写法1.+(2).相应的,其他的方法,比如"Hello".drop(2),也可以用操作符记法,"Hello"

[Scala基础系列 02]Scala函数

本文主要内容如下: 变量和不变量 函数和过程 函数的参数 分号 1.变量和不变量 1.1.变量 Scala的变量分两种,var和val.var,即variable,类似于我们在Java等其他语言中接触到的变量,而val,是value,类似于我们在其他语言中用到的不可重新赋值的常量,或者final变量. 为什么会有这种区别,这是由于很多情况下,其实你不需要一个可变的var,尤其在函数式编程中,更为明显.不变性能给程序带来很多便利,因此Scala非常强调不可变(immutable)的概念.关于不可变

[Scala基础系列 06]Scala类和对象

1.类和构造函数 Scala中的类,基本概念与其他面向对象语言是一致的,不过在语法上有些不一样的地方.与Java等语言相比,Scala的类语法更简洁,使用起来也更方便. 1.1.类的基本语法 我们先来看一个简单的类定义和使用的代码. class ScoreCalculator { private var total, count = 0 def report(score: Int) { total += score count += 1 } def score = if (count == 0)

[Scala基础系列 08]Scala继承、抽象类、接口trait以及AOP实现

1.继承 和java一样,scala采用extends关键字继承基类.代码实例如下: /** * Created by molyeo on 2015/8/11. */ class Person(val name: String, var age: Int) { println("The primary constructor of Person") val school = "BJU" def sleep = "8 hours" override

Spring基础系列10 -- Spring AOP-----------Pointcut, Advisor

Spring基础系列10 -- Spring AOP-----------Pointcut, Advisor 转载:http://www.cnblogs.com/leiOOlei/p/3557643.html 上一篇的Spring AOP Advice例子中,Class(CustomerService)中的全部method都被自动的拦截了.但是大多情况下,你只需要一个方法去拦截一两个method.这样就引入了Pointcut(切入点)的概念,它允许你根据method的名字去拦截指定的method

C#基础系列——小话泛型

前言:前面两章介绍了C#的两个常用技术:C#基础系列——反射笔记 和 C#基础系列——Attribute特性使用 .这一章来总结下C#泛型技术的使用.据博主的使用经历,觉得泛型也是为了重用而生的,并且大部分时候会和反射一起使用.这次还是打算围绕WWH(即What.Why.How)来讲解. 1.什么是泛型:通过参数化类型来实现在同一份代码上操作多种数据类型.利用“参数化类型”将类型抽象化,从而实现灵活的复用.怎么理解呢,其实根据博主的理解,泛型就是将类型抽象化,使用抽象化的类型或对象去实现某些功能