java学习笔记:泛型

Posted 滴滴哒滴哒

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java学习笔记:泛型相关的知识,希望对你有一定的参考价值。

      泛型是一个很重要的概念。其实个人感觉它就像孙悟空保护唐僧画的一个圈,没画圈之前,什么妖魔鬼怪都可以在里面作祟,画圈之后,限定了只有唐僧才能待在里面。

       在java使用泛型之前呢,以ArrayList为例,里面的元素就是Object,这样我们在使用add()方法的时候,无论是什么都会加进去,但是当我们get(),就必须进行强制类型转换。我们想做一个只维护String类型的List,但是java内部维护的却是一个Object的类型List,我们一不小心加入了Integer,在编译的时候不会有任何错误,但是在运行的时候会出现问题(ClassCastException异常)。

      java5以后就引入了“参数化类型”的概念。这样我们在编译的时候就会发现错误,而不是在运行的时候,再去发现错误 。

一、伪泛型

     java的泛型只是“伪泛型”,因为历史原因(兼容性:低版本的程序能在高版本上跑得动)。 看一个例子

public class MyGenericity 
    public static void main(String[] args) 
        ArrayList<String> arrayString=new ArrayList<String>();
        ArrayList<Integer> arrayInteger=new ArrayList<Integer>();
        System.out.println(arrayString.getClass()==arrayInteger.getClass());
    

结果输出是true。明明是两个不同的List,一个是String的List,一个是Integer的List,为什么结果反而是true呢。这是因为java编译器使用了类型擦除来实现,它把泛型版本转换为非泛型版本,所有尖括号之间的信息都被扔掉了。

再通过一个例子,来证明java进行了类型擦除,通过反射的方式来添加元素。

public class MyGenericity 
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException 
        ArrayList<Integer> arrayList3=new ArrayList<Integer>();
        //这样调用add方法只能存储整形,因为泛型类型的实例为Integer
        arrayList3.add(1);
        arrayList3.getClass().getMethod("add", Object.class).invoke(arrayList3, "one");
        for (int i=0;i<arrayList3.size();i++) 
            System.out.println(arrayList3.get(i));
        
    

输出的结果


可以看到我们设置的存储Integer类型的List,却存储了String。

二、类型擦除

       当定义一个泛型类型,都自动提供了一个原始的类型。编译的时候,擦除类型变量,并替换为原始类型(无限定类型则替换为Object(限定一般用extends))。假如有多个限定<T extends Comparable&Serializable>,那么原始类型就是第一个Comparable(这里的extends并不是狭义上的继承,所以可以去连接一个或者多个接口)。

       在虚拟机中没有泛型,所有的类型参数都用它们的原始类型替换。可以看到java的泛型只到编译阶段。所以也会在使用过程中造成一些问题:

       1、不能用基本类型实例化类型参数

       2、运行时类型查询只适用于原始类型

       3、不能实例化类型变量

       4、不能构造泛型数组

       5、泛型类的静态上下文中类型变量无效

       6、不能抛出或捕获泛型类的实例

       7、注意要消除类型擦除引起的一些问题,特别是泛型类中的应用,例如

public class Pair<T>
    public boolean equals(T value)
        //TODO
    

我们在使用的时候以为用的是我们自身定义的方法,其实Object的方法equals()。补救的方式就是重命名方法名。

还有就是当我们使用<? extends A&B> AB两个接口不能是同一接口的不同参数化,这样重名的方法,可能会造成影响。        

可以见得使用泛型的时候,虚拟机内部有类型转换的代码。虽然使用泛型可能使得效率降低,但是为了保障系统的稳定,还是推荐使用泛型。

三、泛型类

       泛型的使用某种意义上来讲,可以增加我们代码的重用性,有时候感觉跟工厂模式有点异曲同工之妙。

public class Apple<T> 
    private T info;

    public Apple()

    public Apple(T info)
        this.info = info;
    

    public void setInfo(T info)
        this.info = info;
    

    public T getInfo()
        return this.info;
    

使用的时候需要我们将T替换成具体的类型。

 public static void main(String[] args) 
        Apple<String> apple1 = new Apple<>("APPLE");
        System.out.println(apple1.getInfo());
        Apple<Integer> apple2 = new Apple<>(33);
        System.out.println(apple2.getInfo());
    

如果要别的类去继承,需要指明T的类型是什么。

public class GreenApple extends Apple<String> 

或者不加菱形符号(调用的时候可能就要用Object去定义取出了)

public class RedApple extends Apple 

四、泛型方法

还可以在普通类中定义泛型方法

public class GenericMethod 
    public static <T> T getMiddle(T t)
        return t;
    

使用

public class GenericDriver 
    public static void main(String[] args) 
        String a = GenericMethod.<String>getResult("aaa");
    


五、泛型通配符

       以字母代替的时候,当我们使用的时候,还是要去确定是什么样的具体类型,如何应对不确定的类型,这时就可以用到我们的通配符?了。

public class GenerictyTest 
    public List<?> getUnKonwn(String s)
        if (s == "Stirng")
            List<String> stringList = new ArrayList<>();
            stringList.add("one");
            return stringList;
        else if (s == "Number")
            List<Integer> intList = new ArrayList<>();
            intList.add(111);
            return intList;
        else 
            List<Long> longList = new ArrayList<>();
            return longList;
        
    

但是通配符的缺点就是你无法在不知道“?”是什么的时候,使用任何与泛型相关的方法,也就是说你无法在这个方法体里面使用add()这种方法,但是可以使用get()方法:Object o = unKnownList.get(0);。

六、带边界的泛型通配符

<? extends ClassName>类型是ClassName本身或者它的子类,上界通配符

<? super ClassName>类型是ClassName本身或者它的父类,下界通配符

用法上extends会使得set()方法失效,但get()方法仍然有用。get()取出来的也只能放在ClassName本身或者它的基类下。

super怎会使得get()方法部分失效,只能放到Object中,set()方法正常。



        

以上是关于java学习笔记:泛型的主要内容,如果未能解决你的问题,请参考以下文章

Java泛型学习笔记 - 泛型类

Java泛型学习笔记 - 泛型方法

Java泛型学习笔记 - 泛型接口

Java泛型学习笔记 - 泛型的继承

Java泛型学习笔记 - 泛型的介绍

Java泛型学习笔记 - 浅析泛型中通配符的使用