Scala中ClassTag的简要记录

先看一段代码

object testClassTag {
  def main(args: Array[String]): Unit = {
    val arr = createArray(1,2)
    println(arr.mkString(","))
  }

  def createArray[T](one: T, two: T) = {
    // 创建以一个数组 并赋值
    val arr = new Array[T](2)    //期望根据T的类型动态生成不同数据类型的数组
    arr(0) = one
    arr(1) = two
    arr
  }
}

这段代码的目的是想传入两个参数,然后使用这两个参数初始化该数组,并且可以根据参数数据类型来动态生成不同的数据类型数组,IDE没有报错,说明没有语法错误,但是编译运行就会报错

cannot find class tag for element type T
    val r = new Array[T](2)

错误提示看起来是代码运行的时候找不到这个T的类型,为什么找不到呢?这里面就涉及到 jvm的类型擦除问题,类Java语言只能在编译的阶段获取到类型参数,一旦代码被送入JVM运行,T的类型信息就被擦除了,但有人要问了,为什么下面这样使用泛型却不会报错

object testSpark {
  def main(args: Array[String]): Unit = {
    var res = new getMessage(10) {}
    println(res.get())
  }
}

abstract class getMessage[T](var param:T){
  def get():T = {
    param
  }
}

这段代码在运行的时候,函数体内只有参数本身,并没有涉及T类型
这就得出一个很粗暴的结论,怎么区分泛型在哪些场合下会被类型擦除,我的总结就是只要方法体或函数体内包含了T,K,V此类泛型标识,必然会有类型擦除。

  • 有没有办法可以不让类型被擦除呢,这个时候 ClassTag 就要上场了
import scala.reflect.ClassTag

object testClassTag {
  def main(args: Array[String]): Unit = {
    val arr = createArray(1,2)
    println(arr.mkString(","))
  }

  def createArray[T:ClassTag](one: T, two: T) = {
    // 创建以一个数组 并赋值
    val arr = new Array[T](2)    //期望根据T的类型动态生成不同数据类型的数组
    arr(0) = one
    arr(1) = two
    arr
  }
}

1.只要在定义泛型函数或泛型类的时候,在泛型标识后加上:ClassTag关键字,并且导入 scala.reflect.ClassTag 包即可

2.或者使用:Manifest关键字,这个不需要导包
加上后,发现此时可以动态生成不同数据类型的数组
最后附上一个使用泛型动态生成数组的函数

def createArray[T: Manifest](arr: T*): Array[T] = {
  val arrs = new Array[T](arr.length) 
  for (item <- 0 until arr.length)
    arrs(item) = arr(item)
  arrs
}

你可能感兴趣的:(Scala中ClassTag的简要记录)