Scala泛型、上界、下界、视图用法

泛型

使用示例

def test={
    
    
  //应用1
  val msg = new IntMessage(1)
  println(msg)
  val msg2 = new StringMessage[String]("class")
  println(msg2)
  //应用2
  val class01 = new EnglishClass[String,String,String]("春季","大数据","初级")
  val class02 = new EnglishClass[String,String,Int]("春季","大数据",1)
  //应用3
  val list1 = List("hello","tom");
  println(midList[String](list1))
  val list2 = List(1,2);
  println(midList[Int](list2))
}

  //应用案例1
abstract  class Message[T](s:T){
    
    
  def get():T ={
    
    
    s
  }
}
class IntMessage[Int](s:Int) extends  Message{
    
    
}
class StringMessage[String](s:String) extends  Message{
    
    
}
  //应用案例2
  class EnglishClass[A,B,C](val season:A,val name:B,val cType:C){
    
    }

//应用案例3
  def midList[E](l:List[E]):E={
    
    
    l(l.length/2)
  }

类型约束上下界

上界

java中泛型表示某个类型是A的子类型,使用extends关键字,这种形式成为上界,语法如下:

<T extends A><? extends A>

scala 中 泛型表示某个类型是A的子类型,使用 <: 语法如下:

[T<:A][_<:A]

上界的应用
编写通用类可以进行Int之间 Float之间等实现了Comparable接口的值的直接比较
代码如下

def test={
    
    
    //传统方式
    val comparaInt = new ComparaInt(10,20)
    println(comparaInt.greater)

    //上限 方式
    //这种方式直接报错,因为 scala 的 Int 类型没有实现 Comparable
    //  val commonCompare1 = new CommonCompare(10,20)
    //  println(commonCompare1.greater)
    //传入 Integer 得到正常结果 20
    val commonCompare2 = new CommonCompare(Integer.valueOf(10),Integer.valueOf(20))
    println(commonCompare2.greater)
    //传入 Int 得到正常结果 20
    //这是因为 predef 中定义了隐式转换,这里声明了 需要 java.lang.Integer 传入了 Int 编译器自动转换
    val commonCompare3 = new CommonCompare[java.lang.Integer](10,20)
    println(commonCompare3.greater)

//使用 视图的时候 可以自动进行 隐式转换 而不必 声明需要 java.lang.Integer
    val commonCompare4 = new CommonCompare2(10,20)
    println(commonCompare4.greater)

  }
  //传统方法
  class ComparaInt(n1:Int,n2:Int){
    
    
    def greater  = if(n1>n2) n1 else n2
  }
  //泛型方法
  class CommonCompare[T <:Comparable[T]](obj1:T,obj2:T){
    
    
    def greater = if(obj1.compareTo(obj2)>0)obj1 else obj2
  }

  //泛型方法 视图 方式
  class CommonCompare2[T <%Comparable[T]](obj1:T,obj2:T){
    
    
    def greater = if(obj1.compareTo(obj2)>0)obj1 else obj2
  }

下界

在java泛型中标识某个类是A类型的父类型,使用 super关键字 ,如下:

<T super A><? super A>

在 Scala 中下界或下限使用 >: 关键字 如下:

 [T >:A][_>:A]

示例

def test={
    
    
  //满足下界的约束  Hello Earth
 sound(Seq(new Earth ,new Earth)).map(_.sound())
  //满足下界的约束 Hello Animal
 sound(Seq(new Animal,new Animal)).map(_.sound())
  //满足下界的约束  Hello Bird   这里虽然不是 Animal的父类也可以满足下界的约束  实际上是将 Bird 转换成了Animal类型
  sound(Seq(new Bird,new Bird)).map(_.sound())

  //如下代码编译执行都没有问题  因此不能用上界的思路去推导,是否满足
  sound(Seq(new Moon,new Moon))
}
  def sound[T>:Animal](things:Seq[T])=things

class Earth{
    
    
  def sound(){
    
    
    println("Hello Earth")
  }
}
  class Animal extends  Earth {
    
    
    override def sound(){
    
    
      println("Hello Animal")
    }
  }
  class Bird extends  Animal {
    
    
    override def sound(){
    
    
      println("Hello Bird")
    }
  }
  class Moon{
    
    
    def sound(){
    
    
      println("Hello Moon")
    }
  }

下界使用总结
对于下界可以传入任意类型,如果传入的是 T 的子类,按照T类型处理,和T无关的类一律按照object处理。
也就是说下界可以随便传参数,只不过处理方式不同而已

视图界定

就是说视图界定会自动采用隐式转换 转换为目标类型,单纯下界的方式不会自动使用隐式转换,但是视图界定可以
语法:[T<%A] 或 <?<%A> :表示如果 定义了传入参数类型和 A 类型的隐式转换那么 会自动调用隐式转换将传入参数转换为A类型
示例:自己写隐式转换结合视图界定的方式比较Person对象的年龄大小

//创建隐式转换,自动将 person 对象转换为Ordered的对象
  implicit  def personToOrderedPerson(p:Person3) = new Ordered[Person3]{
    
    
    override def compare(that: Person3): Int = {
    
    
      p.age-that.age
    }
  }

  def test={
    
    
    val p1 = new Person3("jim",10)
    val p2 = new Person3("tom",12);

    import impli.MyImplicit._
    //创建时自动
    val compareComm3 = new CompareComm(p1,p2)
    println(compareComm3.getter2)

  }
  class Person3(val name:String,val age:Int){
    
    
    override def toString: String = this.name+"  "+this.age
  }


  class CompareComm[T<% Ordered[T]](obj1:T,obj2:T){
    
       //这种写法可以使用隐式转换
    def getter2 =  if(obj1.compareTo(obj2)>0) obj1 else obj2
  }

//  class CompareComm2[T<: Ordered[T]](obj1:T,obj2:T){   //这种写法不能使用隐式转换
//    def getter2 =  if(obj1.compareTo(obj2)>0) obj1 else obj2
//  }

上下文界定

与view bounds一样 context bounds(上下文界定)也是隐式参数的语法糖,为了语法上的方便引入“上下文界定”这个概念。

示例:使用上下文界定+隐式参数的方式,比较两个Person 的年龄大小

//定义隐式值
  implicit  val personComp=  new Ordering[Person4]{
    
    
    override def compare(x: Person4, y: Person4): Int = {
    
    
      x.age-y.age
    }
  }
  class Person4(val name:String,val age:Int){
    
    
    override def toString: String = this.name+"  "+this.age
  }
  //类构造函数的参数可以放在 两个括号中
  //class TT (val name:String)(val age:Int){}
  //方式一 构造参数中使用隐式转换
  //构造函数的参数可以放在两个括号中
  // class CompareComm[T:Ordering](obj1:T,obj2:T)(implicit comparator:Ordering[T]){
    
    
  class CompareComm[T:Ordering](obj1:T,obj2:T)(implicit comparator:Ordering[T]){
    
    
    def greater = if(comparator.compare(obj1,obj2)>0)obj1 else obj2;
  }
  //方式二 将隐式转换放在方法内
  class CompareComm2[T:Ordering](obj1:T,obj2:T){
    
    
    def greater ={
    
    
       def f1 (implicit comparator:Ordering[T]) ={
    
    
         comparator.compare(obj1,obj2)
       }
      if(f1> 0) obj1 else obj2
    }
  }
  //方式三 使用 implicitly 最简单
  class CompareComm3[T:Ordering](obj1:T,obj2:T){
    
    
    def greater ={
    
    
      //这句话本身就是隐式转换,获取到personComp
      implicit val comparator =implicitly[Ordering[T]]
      if(comparator.compare(obj1,obj2)> 0) obj1 else obj2
    }
  }

def test={
    
    
  val p1 = new Person4("jim",10)
  val p2 = new Person4("tom",12);
  //方式一
  val comp = new CompareComm[Person4](p1,p2)
  println(comp.greater)

  //方式二
  val comp2 = new CompareComm2[Person4](p1,p2)
  println(comp2.greater)

  //方式三
  val comp3 = new CompareComm3[Person4](p1,p2)
  println(comp3.greater)
}

协变、逆变和不变

对于一个带泛型参数的类型,比如 List[T],如果对A及其子类型B ,满足List[B]也是List[A]的子类型,那么称之为协变(covariant)
对于一个带泛型参数的类型,比如 List[T],如果对A及其子类型B ,满足List[A]是List[B]的子类型,那么称之为逆变(contravariant)。
如果既不是协变又不是逆变那么就是不变(invariant)。如 List[T],如果对A及其子类型B ,满足List[A]和List[B]没有任何父子关系
在java中所有的泛型都是 invariant ,即List 和 List 并没有任何父子关系。
而Scala中可以在定义类型时,通过语法定义声明为协变或逆变,如果不做声明默认就是不变
如:
List[+T] 在类型声明时定义为协变 这样 List[String] 就是 List[Any]的子类
List[-T] 在类型声明时定义为逆变 这样 List[Any] 就是 List[String]的子类
示例

def test={
    
    
     //协变
     //由于 协变 Tclass[Super] 是Tclass[Sub] 的父类 所以 可以直接赋值不报错
    val t2:Tclass[Super] = new Tclass[Sub](new Sub())
    //同理该行报错 父类型变量不能赋值给子类型
    // val t3:Tclass[Sub] = new Tclass[Super](new Super())

    //逆变
    //由于 协变 Tclass[Super] 是Tclass[Sub] 的子类类 父类不能赋值给子类 所以该行报错
   // val t4:Tclass2[Super] = new Tclass2[Sub](new Sub())
    //同理该行不报错
     val t5:Tclass2[Sub] = new Tclass2[Super](new Super())
    //不变
    //由于 不变 Tclass[Super] 是Tclass[Sub] 没有父子关系 因此  类型不匹配不能相互赋值  所以两行都报错
    // val t6:Tclass2[Super] = new Tclass2[Sub](new Sub())
    //val t7:Tclass3[Sub] = new Tclass3[Super](new Super())
  }
  //定义两个有父子关系的类  Super 和 Sub
  class Super
  class Sub extends  Super
  //定义协变类
  class Tclass[+T](param:T){
    
    }
  //定义逆变类
  class Tclass2[-T](param:T){
    
    }
  //定义不变类
  class Tclass3[T](param:T){
    
    }

猜你喜欢

转载自blog.csdn.net/zhangxm_qz/article/details/108107917
今日推荐