• Java--泛型的原理以及使用场景


    Java从1.5之后支持泛型,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。

    入不支持泛型,则表现为支持Object,不是特定的泛型。

    泛型是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类。可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样。

    可以在集合框架中看到泛型的动机。例如,List类允许您向一个 List添加任意类的对象,即使最常见的情况List.add()。
    因为 list.get() 被定义为返回 Object,所以一般必须将 list.get() 的结果强制类型转换为期望的类型,如下面的代码所示:

    List list = new ArrayList();
    list .add("obj");
    String s = (String) list.get(0);

    要让程序通过编译,必须将 get() 的结果强制类型转换为 String,并且希望结果真的是一个 String。但是有可能某人已经在该映射中保存了不是 String 的东西,这样的话,上面的代码将会抛出 ClassCastException。
    理想情况下,您可能会得出这样一个观点,即 list 是一个 List,它将 String 键映射到 String 值。

    泛型可以消除代码中的强制类型转换,同时获得一个附加的类型检查层,该检查层可以防止有人将错误类型的键或值保存在集合中。这就是泛型所做的工作。

    泛型的好处

    Java 语言中引入泛型是一个较大的功能增强。不仅语言、类型系统和编译器有了较大的变化,以支持泛型,而且类库也进行了大翻修,所以许多重要的类,比如集合框架,都已经成为泛型化的了。

    这带来了很多好处:

    1,类型安全。 泛型的主要目标是提高 Java 程序的类型安全。通过知道使用泛型定义的变量的类型限制,编译器可以在一个高得多的程度上验证类型假设。

    没有泛型,这些假设就只存在于程序员的头脑中(或者如果幸运的话,还存在于代码注释中)。 

    2,消除强制类型转换。 泛型的一个附带好处是,消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。

    3,潜在的性能收益。 泛型为较大的优化带来可能。在泛型的初始实现中,编译器将强制类型转换(没有泛型的话,程序员会指定这些强制类型转换)插入生成的字节码中。

    但是更多类型信息可用于编译器这一事实,为未来版本的 JVM 的优化带来可能。由于泛型的实现方式,支持泛型(几乎)不需要 JVM 或类文件更改。

    所有工作都在编译器中完成,编译器生成类似于没有泛型(和强制类型转换)时所写的代码,只是更能确保类型安全而已。

      Java语言引入泛型的好处是安全简单。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。

        泛型在使用中还有一些规则和限制:
        1、泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。
        2、同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。
        3、泛型的类型参数可以有多个。
        4、泛型的参数类型可以使用extends语句,例如<T extends superclass>。习惯上成为“有界类型”。
        5、泛型的参数类型还可以是通配符类型。例如Class<?> classType = Class.forName(Java.lang.String);
        泛 型还有接口、方法等等,内容很多,需要花费一番功夫才能理解掌握并熟练应用。在此给出我曾经了解泛型时候写出的两个例子(根据看的印象写的),实现同样的 功能,一个使用了泛型,一个没有使用,通过对比,可以很快学会泛型的应用,学会这个基本上学会了泛型70%的内容。

    泛型的使用场景

    通配符

    在开发中对象的引用传递是最常见的,但是在泛型操作中,进行引用传递的时候泛型必须匹配才可以传递,否则无法传递。

    复制代码
    class Info<T>{
    private T var ; // 定义泛型变量
    public void setVar(T var){
    this.var = var ;
    }
    public T getVar(){
    return this.var ;
    }
    public String toString(){
    // 直接打印

    return this.var.toString() ;
    } };
    public class GenericsDemo14{
    public static void main(String args[]){ Info<String> i = new Info<String>() ; // 使用String为泛型类型 i.setVar("MLDN") ; // 设置内容 fun(i) ; } public static void fun(Info<?> temp){ // 可以接收任意的泛型对象 System.out.println("内容:" + temp) ; } };
    复制代码

    使用?可以接受任意类型的数据,却无法进行修改,?w为通配符。

    受限泛型

    复制代码
    class Info<T> { private T var; // 定义泛型变量  public T getVar() { return var; } public void setVar(T var) { this.var = var; } public String toString(){ // 直接打印  return var.toString(); } } public class GenericsDemo17 { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Info<Integer> info1 = new Info<Integer>(); // 声明Integer的泛型对象  Info<Float> info2 = new Info<Float>(); // 声明Float的泛型对象 Info<String> info3 = new Info<String>(); info1.setVar(30); // 设置整数,自动装箱 info2.setVar(30.1F); // 设置小数,自动装箱  info3.setVar("俺是字符串,不能被受限的FUN组装"); fun(info1); fun(info2); // fun(info3); //受限了,不能调用这个  } /** * 可以接收任意的泛型对象(// 只能接收Number及其Number的子类) * @param temp */ public static void fun(Info<? extends Number> temp){ // 只能接收String或Object类型的泛型 //public static void fun(Info<? super String> temp){ System.out.println("内容:"+temp); } }
    复制代码

    不仅仅在使用过程中,也可以在定义类的时候指定泛型上限:

    复制代码
    class Info<T extends Number>{   // 此处泛型只能是数字类型 private T var ; // 定义泛型变量 public void setVar(T var){ this.var = var ; } public T getVar(){ return this.var ; } public String toString(){ // 直接打印 return this.var.toString() ; } }; public class GenericsDemo19{ public static void main(String args[]){ Info<Integer> i1 = new Info<Integer>() ; // 声明Integer的泛型对象  } };
    复制代码

    如果设置成Stirng类型就会出现错误:

    GenericsDemo20.java:15: 类型参数 java.lang.String 不在其限制范围之内 Info<String> i1 = new Info<String>() ; // 声明Integer的 泛型对象

    String 不是Number的子类,最高不能超过Number的子类。

    设置下限

    泛型适用于本类以及父类类型上的时候,必须使用泛型下限。

    如下只能接受String以及String的父类。最低不能接受Stirng类及其父类以外的类。

    复制代码
    class Info<T>{ private T var ; // 定义泛型变量 public void setVar(T var){ this.var = var ; } public T getVar(){ return this.var ; } public String toString(){ // 直接打印 return this.var.toString() ; } }; public class GenericsDemo21{ public static void main(String args[]){ Info<String> i1 = new Info<String>() ; // 声明String的泛型对象 Info<Object> i2 = new Info<Object>() ; // 声明Object的泛型对象 i1.setVar("hello") ; i2.setVar(new Object()) ; fun(i1) ; fun(i2) ; } public static void fun(Info<? super String> temp){ // 只能接收String或Object类型的泛型 System.out.print(temp + "、") ; } };
    复制代码

    注意:子类无法使用父类的泛型类型进行接受。

    复制代码
    class Info<T>{ private T var ; // 定义泛型变量 public void setVar(T var){ this.var = var ; } public T getVar(){ return this.var ; } public String toString(){ // 直接打印 return this.var.toString() ; } }; public class GenericsDemo23{ public static void main(String args[]){ Info<String> i1 = new Info<String>() ; // 泛型类型为String Info<Object> i2 = null ; i2 = i1 ; } };
    复制代码

    就会爆如下错误:

    GenericsDemo23.java:17: 不兼容的类型 找到: Info<java.lang.String> 需要: Info<java.lang.Object> i2 = i1 ; ^

    泛型接口: 

    在jdk1.5以后,不仅仅可以声明泛型类,也可以声明泛型接口,泛型接口很类似泛型类:

    访问权限 +interface +接口名称 + <泛型标示>{}

    泛型接口实现的两种方式

    1:

    复制代码
    interface Info<T>{        // 在接口上定义泛型
        public T getVar() ;    // 定义抽象方法,抽象方法的返回值就是泛型类型 } class InfoImpl implements Info<String>{ // 定义泛型接口的子类 private String var ; // 定义属性 public InfoImpl(String var){ // 通过构造方法设置属性内容 this.setVar(var) ; } public void setVar(String var){ this.var = var ; } public String getVar(){ return this.var ; } }; public class GenericsDemo{ public static void main(String arsg[]){ Info i = null; // 声明接口对象 i = new InfoImpl("soyoungboy") ; // 通过子类实例化对象 System.out.println("内容:" + i.getVar()) ; } };
    复制代码

    2:

    复制代码
    interface Info<T>{ // 在接口上定义泛型 public T getVar() ; // 定义抽象方法,抽象方法的返回值就是泛型类型 } class InfoImpl<T> implements Info<T>{ // 定义泛型接口的子类 private T var ; // 定义属性 public InfoImpl(T var){ // 通过构造方法设置属性内容 this.setVar(var) ; } public void setVar(T var){ this.var = var ; } public T getVar(){ return this.var ; } }; public class GenericsDemo{ public static void main(String arsg[]){ Info<String> i = null; // 声明接口对象 i = new InfoImpl<String>("soyoungboy") ; // 通过子类实例化对象 System.out.println("内容:" + i.getVar()) ; } };
    复制代码

    泛型方法:

    泛型方法定义:

    访问权限 +<泛型标示>+泛型标示 方法名称(泛型标示 参数名称)

    复制代码
    class Demo{ public <T> T fun(T t){ // 可以接收任意类型的数据 return t ; // 直接把参数返回  } }; public class GenericsDemo{ public static void main(String args[]){ Demo d = new Demo()    ; // 实例化Demo对象 String str = d.fun("soyoungboy") ; // 传递字符串 int i = d.fun(30) ; // 传递数字,自动装箱 System.out.println(str) ; // 输出内容 System.out.println(i) ; // 输出内容  } };
    复制代码

    通过泛型方法返回泛型类的实例

    复制代码
    class Info<T extends Number>{ // 指定上限,只能是数字类型 private T var ; // 此类型由外部决定 public T getVar(){ return this.var ; } public void setVar(T var){ this.var = var ; } public String toString(){ // 覆写Object类中的toString()方法 return this.var.toString() ; } }; public class GenericsDemo27{ public static void main(String args[]){ Info<Integer> i = fun(30) ; System.out.println(i.getVar()) ; } public static <T extends Number> Info<T> fun(T param){ Info<T> temp = new Info<T>() ; // 根据传入的数据类型实例化Info temp.setVar(param) ; // 将传递的内容设置到Info对象的var属性之中 return temp ; // 返回实例化对象  } };
    复制代码

     如果同一方法参数使用泛型,应该保证泛型类型一致:

    复制代码
    class Info<T>{ // 指定上限,只能是数字类型 private T var ; // 此类型由外部决定 public T getVar(){ return this.var ; } public void setVar(T var){ this.var = var ; } public String toString(){ // 覆写Object类中的toString()方法 return this.var.toString() ; } }; public class GenericsDemo{ public static void main(String args[]){  Info<Integer> i1 = new Info<Integer>() ; Info<String> i2 = new Info<String>() ; i1.setVar(30) ; // 设置内容 i2.setVar("aoyoungboy") ; // 设置内容  add(i1,i2) ; } public static <T> void add(Info<T> i1,Info<T> i2){ System.out.println(i1.getVar() + " " + i2.getVar()) ; } };
    复制代码

    就会产生错误:

    泛型】_泛型的其他应用代码>javac GenericsDemo.java GenericsDemo29.java:19: 无法将 GenericsDemo 中的 <T>add(Info<T>,Info<T>) 应用 于 (Info<java.lang.Integer>,Info<java.lang.String>) add(i1,i2) ; ^

    泛型数组

    使用泛型方法的时候,也可以传递或者返回一个泛型数组:

    复制代码
    public class GenericsDemo{ public static void main(String args[]){ Integer i[] = fun1(1,2,3,4,5,6) ; // 返回泛型数组  fun2(i) ; } public static <T> T[] fun1(T...arg){ // 接收可变参数 return arg ; // 返回泛型数组 } public static <T> void fun2(T param[]){ // 输出 System.out.print("接收泛型数组:") ; for(T t:param){ System.out.print(t + "、") ; } } };
    复制代码

     泛型嵌套:

    复制代码
    class Info<T,V>{ // 接收两个泛型类型 private T var ; private V value ; public Info(T var,V value){ this.setVar(var) ; this.setValue(value) ; } public void setVar(T var){ this.var = var ; } public void setValue(V value){ this.value = value ; } public T getVar(){ return this.var ; } public V getValue(){ return this.value ; } }; class Demo<S>{ private S info ; public Demo(S info){ this.setInfo(info) ; } public void setInfo(S info){ this.info = info ; } public S getInfo(){ return this.info ; } }; public class GenericsDemo{ public static void main(String args[]){  Demo<Info<String,Integer>> d = null ; // 将Info作为Demo的泛型类型 Info<String,Integer> i = null ; // Info指定两个泛型类型 i = new Info<String,Integer>("李兴华",30) ; // 实例化Info对象 d = new Demo<Info<String,Integer>>(i) ; // 在Demo类中设置Info类的对象 System.out.println("内容一:" + d.getInfo().getVar()) ; System.out.println("内容二:" + d.getInfo().getValue()) ; } };
    复制代码
     
    android中类似于定义Adapter的时候传入model的泛型
  • 相关阅读:
    JS内容左右滑动
    JS返回上一页
    两栏 三栏的css
    舅舅去世
    .net学习开始
    以论坛管理的方式来处理公司资讯
    《尽管去做》摘
    网页视频播放器代码集
    火影忍者和海贼王
    古代风水文献
  • 原文地址:https://www.cnblogs.com/xunzhi/p/5683709.html
Copyright © 2020-2023  润新知