scala学习笔记 - 类型参数(一)

25 篇文章 0 订阅
15 篇文章 2 订阅

泛型类

类和特质可以带类型参数,Scala中,我们用方括号来定义类型参数,例如:

class Pair[T, S](val first: T, val second: S)

以上将定义一个带有两个类型参数T和S的类。在类的定义中,你可以用类型参数来定义变量、方法参数、以及返回值的类型。
Scala会从构造参数推断出实际类型:

val p = new Pair(30, "aaaa") // 这是一个Pair[Int, String]

你也可以自己指定类型:

val p2 = new Pair[Any, Any](30, "aaaa")

泛型函数

函数和方法也可以带类型参数。以下是一个简单的示例:

def getMiddle[T](a: Array[T]) = a(a.length / 2)

和泛型类一样,你需要把类型参数放在方法名之后。Scala会从调用该方法使用的实际参数来推断出类型。

getMiddle(Array ("Mary", "had", "a", "little", "lamb")) // 将会调用getMiddle[String]

如有必要,你也可以指定类型:

val f = getMiddle[String] _ // 这是具体的函数,保存到f

类型变量界定

有时,你需要对类型变量进行限制。虑这样一个Pair类型,它要求自己的两个组件类型相同,就像这样:

class  Pair[T](val first: T, val second: T)

现在你想要添加一个方法,输出较小的那个值:

class Pair [T](val first: T, val second: T) {
  def smaller = if (first.compareTo(second) < 0) first else second // 错误
}

这是错的,我们并不知道first是否有compareTo方法。要解决这个问题,我们可以添加一个上界T <: Comparable[T]

class Pair [T <: Comparable[T]](val first: T, val second: T) {
  def smaller = if (first.compareTo(second) < 0) first else second // 错误
}

这意味着T必须是Comparable[T]的子类型。
你也可以为类型指定一个下界;举例来说,假定我们想要定义一个方法,用另一个值替换对偶的第一个组件,我们的对偶是不可变的,因此需要返回一个新的对偶,如下:

class Pair [T](val first: T, val second: T) {
  def replaceFirst(newFirst: T) = new Pair[T](newFirst, second)
}

可以有更好的方法;通常而言,替换进来的类型必须是原类型的超类型。

def replaceFirst[R >: T](newFirst: R) = new Pair[R](newFirst, second)
// 也可以写成:
def replaceFirst[R >: T](newFirst: R) = new Pair(newFirst, second)
// 返回值会被正确推断为new Pair[R]

注意:如果你不写上界,def replaceFirst[R](newFirst: R) = new Pair(newFirst, second),可以通过编译,但它将返回Pair[Any]。

视图界定

class Pair [T <: Comparable[T]]

如果new一个Pair(4, 2),,编译器会抱怨说Int不是Comparable[Int]的子类型。和java.lang.Integer包装类型不同,Scala的Int类型并没有实现Comparable。不过,RichInt实现了Comparable[Int],同时还有一个从Int到RichInt的隐式转换。
解决方法是使用“视图界定(view bound)”,就像这样:

class Pair [T <% Comparable[T]]

<%意味着T可以被隐式转换成Comparable[T]。不过,Scala的视图界定即将退出历史舞台,如果你在编译时打开-future选项,使用视图界定将收到编译器的警告,你可以用“类型约束( type constraint )”替换视图界定,就像这样:

class Pair [T](val first: T, val second: T)(implicit ev: T => Comparable[T]) {
  def smaller = if (first.compareTo(second) < 0) first else second
  ...
}

上下文界定

视图界定T <% V要求必须存在一个从T到V的隐式转换。上下文界定(context bound)的形式为T:M,其中M是另一个泛型类。它要求必须存在一个类型为M[T]的“隐式值(implicit value)”。例如:

class Pair[T : Ordering]

上述定义要求必须存在一个类型为Ordering[T]的隐式值。该隐式值可以被用在该类的方法中,当你声明一个使用隐式值的方法时,需要添加一个 “隐式参数(implicit parameter)”。如下示例:

class Pair [T : Ordering](val first: T, val second: T){
  // 隐式值比隐式转换更为灵活
  def smaller(implicit ord: Ordering[T]) = if (ord.compare(first, second) < 0) first else second
}

ClassTag上下文界定

要实例化一个泛型的Array[T],我们需要一个ClassTag[T]对象。要想让基本类型的数组能正常工作的话,这是必需的。举例来说,如果T是Int,你会希望虚拟机中对应的是一个int[]数组,如果你编写的是一个构造泛型数组的泛型函数,则你需要帮它一下 ,传给它那个类标签(class tag)对象,用上下文界定即可,就像这样:

import scala.reflect._
def makePair[T: ClassTag](first: T, second: T){
  val r = new Array[T](2);
  r(0) = first
  r(1) = second
  r
}

如果你调用makePair(4,9),编译器将定位到隐式的ClassTag[T]并实际上调用makePair(4,9)(classTag)。这样一来,该方法调用的就是classTag.newArray,在本例中这是一个将构造出基本类型数组int[2]的ClassTag[Int]。在虚拟机中,泛型相关的类型信息是被抹掉的,这时只会有一个makePair方法,其却要处理所有类型T。

参考:快学scala(第二版)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值