深入浅出Java和Scala中类类型及泛型系统

8 篇文章 0 订阅

对于Scala来说 Scala类型相关的概念要比Java中要复杂。Scala中的类型信息相对复杂,在java中运行时对范型的处理不太友好,会把泛型擦除,Scala的泛型系统就设计的较为巧妙,而且对类类型的管理这方面还是比较好的。下面就结合scala和java中类及泛型相关的问题进行梳理。

 

Class类类型/Class类对象

这里先捋清楚一些概念 别绕晕了。

比如我们定义了一个类 A , 用A去new出来一个对象a,获取到的就是A的对象a,就可以说a的类型是A。

那么万物皆对象的java里,这个A被我们定义成一个类型,类型也是对象,那它是谁的对象呢?又怎么获取A类的类型呢?或者说是怎么获取它的类型类呢?

1⃣️java里有两种办法  a.getClass  以及  A.class  比如我们知道了a是A的对象就直接a.getClass() 如果没有a呢?只知道A 那就A.class,且a.getClass()==A.class 这点会和下面scala获取类类型做个对比。 注意:类型类是一一对应的,父类的类型类和子类的类型类是不同的。再详细点的参考[https://blog.csdn.net/zhawabcd/article/details/78289140]

2⃣️在scala中的类没有 A.class这个方法 对应的是classOf[A],这个classOf[A]获取的类类型是明确的A的类类型,或者a.getClass方法获取,getClass方法得到的是 Class[A]的某个子类,虽然比较的时候这两个类类型也是相等的,差别体现在类型赋值时无法把一个 Class[_ < : A] 赋值给一个 Class[A],因为java里的 Class[T]是不支持协变的,参看下面代码,这里注意下:在scala中是类型是存在边界的。

scala> class  A
scala> val a = new A

scala> a.getClass
res2: Class[_ <: A] = class A

scala> classOf[A]
res3: Class[A] = class A

 

当然我们也可以通过反射获取某个类,反射就不具体说了,以后再总结出来~

 

泛型

泛型的概念,好比为你的方法中参数加入个类型参数,用于为参数指定某种类型, 这个类型是不确定的,java在编译的时候不会看出来【泛型擦除】,scala中可以通过TypeTag来记录泛型信息,从而解决泛型擦除问题,关于scala中泛型还涉及到协变 ,逆变,边界等等概念,可以限定类型的范围,不在赘述,可以google参考。

在scala中还有ClassTag  存着很多type 可以直接用  但是有些自定义类型,复杂类型比如Map[String,Array[Long]]这种  我想获取这个类型怎么办?

 

scala中type  typeof[T]  TypeTag ClassTag[T]的用法和区别

 

type

scala的关键字 可以声明类型,为类型起别名 比如 type Me = String 则 Me就可以当成是String类型的替代  如 var str:Me="丁郁非"

通常定义复杂或组合类型比较实用  type KeyType=(String,Int)  在复杂场景下使用起来代码很简洁了。

 

typeOf[T]

与classOf相比,typeOf返回的泛型信息更具体 可以用作类型的判断 例如上面图中的例子

scala> classOf[List[Int]]
res8: Class[List[Int]] = class scala.collection.immutable.List
 
scala> classOf[List[Int]] == classOf[List[String]]
res9: Boolean = true
 
scala> typeOf[List[Int]] == typeOf[List[String]]
res10: Boolean = false

 

TypeTag

‘TypeTag’是一个[[scala.reflect.api]。附加的静态保证所有类型引用都是具体的,也就是说,它不是包含任何对未解析的类型参数或抽象类型的引用。解决了Scala的类型在运行时被擦除的问题(类型擦除)   众所周知 java/scala在运行的时候范型会被擦出 假如有这样一段代码

结果显然不对的 应该输出dyf 这就是范型在运行的时候被擦除了 当我们使用TypeTag就可以解决这个问题啦

首先导包~别导错了

import scala.reflect.runtime.universe._

这里=:=(类型相等)和<:<(子类型关系)用于等式检查 ,判断类型是否相同,不要使用==!=。

其次,TypeTags 是线程不安全的 在多线程环境下可以使用case class 来代替这种需要类型的 比如定义一个

case class A(a:Any)

a match {

 case _:A(s:String)=>....

 case _:A(s:Int)=>....

}
      Java和scala是基于JVM的,在虚拟机内部,并不关心泛型或类型系统。在JVM上,泛型参数类型T在运行时是被擦除掉的, 编译器把T当作Object来对待,所以T的具体信息是无法得到的。
       Manifest是scala2.8引入的一个特质,用于编译器在运行时也能获取泛型类型的信息。为了使得在运行时得到T的信息,scala需要额外通过Manifest来存储T的信息,并作为参数用在方法的运行时上下文。不过scala在2.10里用TypeTag替代了Manifest,用ClassTag替代了ClassManifest(Manifest的弱化版),原因是在路径依赖类型中,Manifest存在问题

更多详见参考【http://hongjiang.info/scala-type-system-manifest-vs-typetag/】【https://www.iteblog.com/archives/1520.html

 


ClassTag[T]

ClassTag是TypeTag的一个弱化的版本,所谓的弱是指类型信息不如TypeTag那么完整,运行时保存类型信息T,但ClassTag只提供在运行时创建类型所需的信息(类型会被擦除的范型类)。ClassTags是由只知道类型的顶级类构造而成,而不一定知道它的所有参数类型,通过参见SparkContext发现大量使用ClassTag保存泛型信息,没有使用TypeTag。所以一般ClassTag就足以满足我们的需要。

 

总结

ClassTag:当调用方法传递参数执行时,ClassTag会把参数类型记住,其实ClassTag就是隐式值,将类型传递给T,这就是隐式转换最重要的工作

 

Manifest(上下文界定)特点:

1、Array在Scala中是一个原生的数组,所以编译时并不会有过多的辅助的处理

2、通过Manifest可以创建Array泛型类型的创建,即可以帮助Array提供确认泛型的类型(这就是隐式值)

3、Manifest会存储运行时实际的类型,然后运行时作为参数传递

4、泛型类型在运行时具体的类型是看不到的,被抹掉了

5、隐式是一个难点、重点,隐式是自动判断类型,且进行传递的一个过程

 

ClassManifest:和Manifest具有相同的作用,但是获取信息方面比Manifest弱一些

 

ClassTag :

1、在Spark中的实际编码中ClassTag用的比较频繁,在隐式转换中具有非常重要的作用

2、ClassTag最重要的也是指定在运行时泛型的类型(编译时无法识别的type)

 

 

综合比较:

1、Manifest有一些弱点,在判断指定路径方面的class的类型时会出现有误,所以用ClassTag代替Manifest,用TypeTag代替ClassManifest

2、TypeTag要比ClassTag更加强大(在实际应用中ClassTag更加广泛)

3、所有在JVM上运行的泛型的类型是被抹掉的,虚拟机根本不知道是什么类型

4、在实际的开发中经常使用上面截图中的第一种方式应用泛型类,但在实际的编译中会形成第二种的方式运行

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值