Scala--泛型-★★

  • Scala中的泛型和Java中的泛型类似,都是用来进行类型约束的
  • 父类类型可以接收子类对象(. getclass得到的仍然是子类字节码)
  • 但是Scala中的泛型比Java中的更加强大!

Java中的泛型

  • 通过几个面试题来复习
package cn.hanjiaxiaozhi.genericdemo;import java.util.ArrayList;
import java.util.List;/**
 * Author hanjiaxiaozhi
 * Date 2020/7/17 14:26
 * Desc 演示Java面试题顺便复习Java泛型
 */
public class Generic_Java {
    
    
    public static void main(String[] args) {
    
    
        List list = new ArrayList<>();
        list.add(110);
        list.add("jack");
        list.add(3.14);
        System.out.println(list);//[110, jack, 3.14]
        //问题1:上面的代码是否编译通过?--可以,因为没有加泛型约束
        //问题2:那既然list中可以存放任意类型,那么开发时直接这样用多好?
//为什么还要泛型呢?--泛型可以约束可以存哪些数据,也可以方便操作数据
        /*for (Object o : list) {
            //如果想要在这里调用String特有的方法,不方便
            String s = ((String) o).toUpperCase();
            System.out.println(s);
            //同样的调用其他的如Integer的特有方法也不方便...
        }*/
​
        List<String> list2 = new ArrayList();
        list2.add("110");
        list2.add("jack");
        list2.add("3.14");
        System.out.println(list2);//[110, jack, 3.14]
        for (String str : list2) {
    
    
            String s = str.toUpperCase();
            System.out.println(s);
            //110
            //JACK
            //3.14
        }
​
        System.out.println("=============");
        //Integer extends Number
        Integer i1 = 1;
        Number i2 = 2;
        i2 = i1;
        //问题3:上面这行代码对不对?---对的,因为父类类型可以接收子类对象!
​
        List<Integer> l1 = new ArrayList<>();
        List<Number> l2 = new ArrayList<>();
        //l2 = l1;//编译错误
        //问题4:上面l2 = l1;这行代码对不对?--不对,编译就失败,因为l1和l2没有继承关系(也就是类的继承并不不看他们的泛型)
        //注意:在Java中,类的继承不看泛型,更专业的说法叫,Java的泛型不支持协变//m1(l1);//不对
        //m1(l2);//对的
        //问题5:上面的代码对不对?---m1(l1);//不对   m1(l2);//对的//问题6:如果就是想要将l1传给方法怎么办?--使用泛型上下界
        m2(l1);
        m2(l2);
​
        m3(l1);
        m3(l2);//问题7:l1和l2的字节码对象是否一样?true
        System.out.println(l1.getClass());//class java.util.ArrayList
        System.out.println(l2.getClass());//class java.util.ArrayList
        System.out.println(l1.getClass() == l2.getClass());//true?false? ---true
        //Java中的泛型是"假泛型",在运行的时候就没了,也叫做泛型擦除}
​
    public static void m1(List<Number> list){
    
    
        System.out.println(list);
    }
    //? extends Number 表示泛型上界为Number,也就是传递过来的参数的泛型必须是Number或继承自Number
    public static void m2(List<? extends Number> list){
    
    
        System.out.println(list);
    }//? super Integer 表示泛型的下界为Integer,也就是传递过来的参数的泛型必须是Integer或是Integer的父
    public static void m3(List<? super Integer> list){
    
    
        System.out.println(list);
    }
}

Scala中的泛型类和泛型方法-能掌握更好-★★

  • scala中,使用方括号来定义类型参数
  • 泛型方法
def 方法名[泛型名称](..) = {
    
    

    //...

}
  • 泛型类
class[T](val 变量名: T)
package cn.hanjiaxiaozhi.genericdemo
​
/**
 * Author hanjiaxiaozhi
 * Date 2020/7/17 15:10
 * Desc 演示Scala中的泛型类和泛型方法
 */
object Generic_Demo {
    
    
  def main(args: Array[String]): Unit = {
    
    
    println(args.toBuffer)
    //创建对象并调用方法
    val tool = new Tools()
    val res: Int = tool.getMiddle(Array(1,2,3,4,5))
    println(res)//3
    val res2: String = tool.getMiddle2(Array[String]("a","b","c"))
    println(res2)//bval kv1 = KV("abc","def")
    val kv2 = KV(123,456)
    val kv3 = KV(123,"789")
    println(kv1)
    println(kv2)
    println(kv3)
  }
}class Tools{
    
    
  //定义一个普通方法--返回数组的中间元素
  def getMiddle(arr:Array[Int]) = {
    
    
    arr(arr.length / 2)
  }//定义一个泛型方法
  def getMiddle2[T](arr:Array[T]):T={
    
    
    arr(arr.length / 2)
  }
}//定义一个带泛型的样例类,K和V都是带泛型的,可以接收任意类的参数
case class KV[A,B](name:A,value:B)

Scala中的泛型上下界-能掌握更好-★★

  • 引入
    • 在指定泛型类型时,有时需要界定泛型类型的范围,而不是接收任意类型。
    • 比如,要求某个泛型类型,必须是某个类的子类,这样在程序中就可以放心的调用父类的方法,程序才能正常的使用与运行。
    • 此时,就可以使用上下边界Bounds的特性;
    • Scala的上下边界特性和Java中一样,允许泛型类型是某个类的子类,或者是某个类的父类;
  • 上下界
    • S <: T 类似于Java中的 ? extends T
      • 类型上界,也就是S必须是类型T的子类(或本身)
    • U >: T 类似于Java中的 ? Super T
      • 类型下界,也就是U必须是类型T的父类(或本身)
package cn.hanjiaxiaozhi.genericdemo
​
/**
 * Author hanjiaxiaozhi
 * Date 2020/7/17 15:26
 * Desc 
 */
//定义一些有子父关系的类
class Person
class Student extends Person
class Policeman extends Person //警察类
class TrafficPolice extends Policeman //交警object Generic_Demo2 {
    
    
  //泛型的上界T <: 类型
  //要求:m1的arr只能接收泛型的上界为Person的类型
  //也就是必须是Person类及其子类(Person或继承自Person)
  //那么使用T <:Person即可
  //相当于Java中的 ? extends Person
  def m1[T <: Person](arr:Array[T])={
    
    
    println(arr.toBuffer)
  }//泛型的下界T >: 类型
  //需求:m2中的arr只能接收泛型的下界为Policeman的类型
  //也就是必须是Policeman的类及其父类
  //那么使用 T>:Policeman即可
  //相当于Java中的 ? super Policeman
  def m2[T>:Policeman](arr:Array[T])={
    
    
    println(arr.toBuffer)
  }
​
​
  def main(args: Array[String]): Unit = {
    
    
    m1(Array(new Person))//正确
    m1(Array(new Student))//正确
    //m1(Array("abc"))//错误,因为String不符合T <: Person
​
    m2(Array(new Policeman))//正确
    m2(Array(new Person))//正确


//重点来了
 m2(Array(new Policeman,new TrafficPolic))//也可以


    //m2(Array(new "abc"))//错误,因为String不符合T <: Person
    //m2(Array(new TrafficPolice))//编译没报错,但是运行的时候是错的(和编译器有关系),
//因为TrafficPolice不符合T <: Person
  }
}

Scala中的协变、逆变、非变-了解-★

  • 注意:
    • 函数的参数类型是逆变的,而函数的返回类型是协变的
  • 引入
    • 由于参数化类型的参数(参数类型)是可变的,当两个参数化类型的参数是继承关系(可泛化),那被参数化的类型是否也可以泛化呢?
  • Java中这种情况下是不可泛化的.
    • 在Java中,如果有 A是 B的子类,但 Card[A] 却不是 Card[B] 的子类
    • 而在Scala 中只要灵活使用协变与逆变,就可以解决此类 Java 泛型问题;且Scala提供了三个选择,即协变(+)、逆变(-)和非变。
    • Scala 中的协变和逆变主要是用来解决参数化类型的泛化问题,解决了Java中泛型的一大缺憾
  • 协变(+)、逆变(-)和非变
    • 协变:有子父关系的类型,作为泛型时,所属的类也有子父关系
      • C[+T]:如果A是B的子类,那么C[A]是C[B]的子类。
    • 逆变:有子父关系的类型,作为泛型时,所属的类的子父关系颠倒了
      • C[-T]:如果A是B的子类,那么C[A]是C[B]的父类。
    • 非变:有子父关系的类型,作为泛型时,所属的类的无子父关系
      • C[T]: 无论A和B是什么关系,C[A]和C[B]没有从属关系。

概念

  • 注意:
    • 在Java中,如果A是B的子类,但是XX[A]并不是XX[B]的子类,如:
List<Integer> l1 = new ArrayList<>();
List<Number> l2 = new ArrayList<>();
//l2 = l1;//编译错误
  • 但是,在Scala中,
  • 如果A是B的子类
  • 通过协变、逆变、非变可以改变XX[A]和XX[B]的关系,
    • XX[A]是XX[B]的子----(协变)
    • XX[A]是XX[B]的父----(逆变)
    • XX[A]和XX[B]没有子父关系-----(非变)
  • 代码演示
package cn.hanjiaxiaozhi.genericdemo
​
/**
 * Author hanjiaxiaozhi
 * Date 2020/7/17 15:47
 * Desc 演示Scala中的协变、逆变、非变
 */
class B//定义一个父类class A extends B //定义一个子类继承父类
//A是B的子类
//XX1[A]是XX1[B]的子类---协变
//XX2[A]是XX2[B]的父类---逆变
//XX3[A]和XX3[B]没有关系---非变//+表示协变
class XX1[+T](msg:String)
//-表示逆变
class XX2[-T](msg:String)
//没有+或-表示非变
class XX3[T](msg:String)
​
​
object Generic_Demo3 {
    
    
  def main(args: Array[String]): Unit = {
    
    
    //XX1是支持协变的,A是B的子类,所以XX1[A]也是XX1[B]的子类
    //所以可以使用父类类型接收子类对象
    val x1: XX1[B] = new XX1[A]("xx1")
    //XX2是支持逆变的,A是B的子类,所以XX1[A]是XX1[B]的父类
    //所以可以使用父类类型接收子类对象
    val x2: XX2[A] = new XX2[B]("xx2")
    //XX3是非变,也就是不支持逆变,也不支持非变,所以XX3[A]和XX3[B]没有关系,不能相互赋值
    //val x3: XX3[A] = new XX3[B]("xx3")}
}

面试题

  • java中—下面代码不正确
class B{
    
    }//定义一个父类class A extends B{
    
    } //定义一个子类继承父类
​
List<A> lista = new ArrayList<>();
List<B> listb = new ArrayList<>();
listb = lista;//正确还是错误? --错误,因为Java中没有协变
  • scala中–下面代码正确
class B//定义一个父类class A extends B //定义一个子类继承父类var lista  = List[A]()
var listb  = List[B]()   
listb = lista;//正确还是错误? --正确,因为Scala的List源码为List[+A] 支持协变

猜你喜欢

转载自blog.csdn.net/qq_46893497/article/details/114043793