scala-泛型

本文深入探讨Scala中的泛型概念,包括泛型的基本使用、类型边界(上边界与下边界)、视图边界、动态类型、逆变与协变及排序特性等。通过具体的代码示例,帮助读者更好地理解和应用这些高级特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Scala学习——泛型[T]

6种使用

复制代码
 1 package com.dtspark.scala.basics
 2 
 3 /**
 4  * 1,scala的类和方法、函数都可以是泛型。
 5  * 
 6 
 7  * 2,关于对类型边界的限定分为上边界和下边界(对类进行限制)
 8  * 上边界:表达了泛型的类型必须是"某种类型"或某种类型的"子类",语法为“<:”,
 9  * 下边界:表达了泛型的类型必须是"某种类型"或某种类型的"父类",语法为“>:”,
10  * 
11 
12  * 3, "<%" :view bounds可以进行某种神秘的转换,把你的类型在没有知觉的情况下转换成目标类型,
13  * 其实你可以认为view bounds是上下边界的加强和补充,语法为:"<%",要用到implicit进行隐式转换(见下面例子)
14  * 
15 
16  * 4,"T:classTag":相当于动态类型,你使用时传入什么类型就是什么类型,(spark的程序的编译和运行是区分了Driver和Executor的,只有在运行的时候才知道完整的类型信息)
17  * 语法为:"[T:ClassTag]"下面有列子
18  *
19 
20  * 5,逆变和协变:-T和+T(下面有具体例子)+T可以传入其子类和本身(与继承关系一至)-T可以传入其父类和本身(与继承的关系相反),
21  * 
22 
23  * 6,"T:Ordering" :表示将T变成Ordering[T],可以直接用其方法进行比大小,可完成排序等工作
24  */
25 
26 class Person(val name:String){
27   def talk(person:Person){
28     println(this.name+" speak to "+person.name)
29   }
30 }
31 
32 class Worker(name:String)extends Person(name)
33 
34 class Dog(val name:String)
35 
36 
37 
38 
39 //注意泛型用的是[]
40 class Club[T<:Person](p1:T,p2:T){//"<:"必须是person或person的子类
41   def comminicate = p1.talk(p2)
42 }
43 
44 
45 class Club2[T<%Person](p1:T,p2:T){
46   def comminicate = p1.talk(p2)
47 }
48 
49 class Engineer
50 class Expert extends Engineer
51 //如果是+T,指定类型为某类时,传入其子类或其本身
52 //如果是-T,指定类型为某类时,传入其父类或其本身
53 class Meeting[+T]//可以传入T或T的子类
54 
55 class Maximum[T:Ordering](val x:T,val y:T){
56   def bigger(implicit ord:Ordering[T])={
57     if(ord.compare(x, y)>0)x else y
58   }
59 }
60 
61 object HelloScalaTypeSystem {
62   def main(args: Array[String]): Unit = {
63      val p= new Person("Spark")
64      val w= new Worker("Scala")
65      new Club(p,w).comminicate
66      
67 //"<%"的列子
68      //只是提供了一个转换的方法,在遇到<%时会调用看dog是否被转换了。
69      implicit def dog2Person(dog:Dog)=new Person(dog.name)
70      val d = new Dog("dahuang")
71      //注意必须强制类型转换,implicit中虽然是将dog隐式转换成person,
72      //但是其实是对象擦除,变成了object,所以还要强制类型转换成person后才能使用
73      //用[person]强制转换
74      new Club2[Person](p,d).comminicate
75      
76  //-T +T例子,下面的participateMeeting方法指定具体是什么泛型
77      val p1=new Meeting[Engineer]
78      val p2=new Meeting[Expert]
79      participateMeeting(p1)
80      participateMeeting(p2)
81  // T:Ordering 的例子   
82      println(new Maximum(3,5).bigger)
83      println(new Maximum("Scala","Java").bigger)
84 
85      
86      
87   }
88   //这里指定传入的泛型具体是什么
89   def participateMeeting(meeting:Meeting[Engineer])=  println("welcome")
90   
91 }
复制代码

 

T:ClassTag的例子(根据输入动态定义)

命令行代码:

scala> import  scala.reflect.ClassTag
import  scala.reflect.ClassTag
 
scala> def  mkArray[T :  ClassTag](elems :  T*) =  Array[T](elems :  _ *)
mkArray :  [T](elems :  T*)( implicit  evidence$ 1 :  scala.reflect.ClassTag[T])Array[T]
 
 
scala> mkArray( 1 , 2 , 3 )
res 1 :  Array[Int] =  Array( 1 , 2 , 3 )
 
scala> mkArray( "ss" , "dd" )
res 2 :  Array[String] =  Array(ss, dd)
scala> mkArray( 1 , "dd" )
res 2 :  Array[Any] =  Array( 1 , dd)
Scala 中,`object` 是单例对象,它本质上是一个具体的实例,而不是类。由于是基于类参数化的机制,而单例对象在定义时必须是具体的类,因此不能直接在 `object` 上使用参数。尝试在 `object` 中使用会导致编译错误,例如 **“class type required but T found”**,因为参数 `T` 在运行时会被类擦除,并且 `object` 无法携带类参数信息[^1]。 ### 与 `object` 的限制 Scala 的 `object` 是静态单例的,它在编译时就被具体化为一个实例。因此,不能像类或方法那样,在 `object` 定义中使用类参数。例如,以下代码会引发编译错误: ```scala object MyGenericObject[T] { // 编译错误:object cannot have type parameters def get: T = ??? } ``` 该错误的根本原因在于 `object` 必须是静态且唯一的,而需要在实例化时传入具体的类参数。由于 `object` 无法被参数化,因此不能直接定义 `object` [^3]。 ### 替代方案 虽然不能直接定义 `object`,但可以通过以下方式实现类似功能: #### 1. 使用方法 可以在 `object` 内部定义方法,以实现对不同类的操作: ```scala object GenericUtil { def identity[T](x: T): T = x } val result1 = GenericUtil.identity[Int](42) // 返回 Int val result2 = GenericUtil.identity[String]("abc") // 返回 String ``` 此方式利用了方法级别的,允许在调用时传入类参数,从而实现类安全的多态行为[^3]。 #### 2. 使用类类模式 结合隐式参数和方法,可以实现更高级的行为,例如: ```scala trait Serializer[T] { def serialize(value: T): String } object Serializer { implicit val intSerializer: Serializer[Int] = _.toString implicit val stringSerializer: Serializer[String] = identity } object GenericUtil { def serialize[T](value: T)(implicit serializer: Serializer[T]): String = serializer.serialize(value) } val s1 = GenericUtil.serialize(123) // 使用 Int 的 Serializer val s2 = GenericUtil.serialize("hello") // 使用 String 的 Serializer ``` 这种方式通过隐式解析机制,实现了类驱动的行为扩展,是 Scala 中常见的编程模式之一[^4]。 #### 3. 使用辅助类封装逻辑 如果需要维护状态或更复杂的逻辑,可以将逻辑封装在一个类中,并通过 `object` 提供工厂方法或辅助函数: ```scala class Box[T](val value: T) object Box { def apply[T](value: T): Box[T] = new Box(value) } val box1 = Box(42) // Box[Int] val box2 = Box("hello") // Box[String] ``` 此方式利用 `object` 提供类的构造辅助,避免直接在 `object` 上使用参数,同时保持类安全和简洁的调用方式[^3]。 ### 总结 Scala 的 `object` 本质上是静态单例,不能携带类参数,因此不能直接定义 `object`。但可以通过定义方法、结合隐式参数实现类类模式,或者通过辅助类封装逻辑,来实现类似行为。这种方式既保留了类安全,又符合 Scala 的类系统设计原则。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值