java泛型应用详解

版权声明:如有转载,请注明地址! https://blog.csdn.net/u013521882/article/details/85263295

在平时的开发过程中,很多哥们不清楚泛型的概念以及用法,所以很多时候,写了很多没必要的代码,又或者,看到别人在使用泛型的时候,感觉对方很牛逼。。。
其实,泛型没你想象的那么难,follow me
一、为什么要使用泛型
我们来看个简单的实例

public class Test {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("hah");
        //list.add(new Test());
        // list.add(1);
        for (Object object : list) {
            String s1 = (String)object;//1
            //.....如果是你你该如何拿出list的值,如果list中放着上边的不同类型的东西。无解
        }
    }
}

以上,在编码过程中,主要存在两个问题:
1.当我们将一个对象放入集合中,集合不会记住此对象的类型,当再次从集合中取出此对象时,改对象的编译类型变成了Object类型,但其运行时类型任然为其本身类型。
2.为了可以正确获取list中的元素,需要人为的强制类型转化到具体的目标类型,多写了很多代码不说,且很容易出现“java.lang.ClassCastException”异常。
怎么解决以上的问题呢?答案:泛型

二、泛型的概念及应用
泛型,通俗说,不确定具体的数据类型,可以是自定义的数据类型,也可以是jdk中的数据类型。而且使用者也不需要关心具体的数据类型,这就是它的价值。
1.方法使用泛型
常见格式:public  <T> List<T> getValue(T t){...}

(1)<T>可以理解为泛型的声明,你只有声明了T,你才可以在方法中用到T这一具体的类型,实际使用中,你可以看到有同时声明很多种的,比如:T、E、K、V、R、L等,这个要看你具体的应用场景了,可以灵活使用。
如:

public class SpecificClass {
    // 入参非泛型,返回值泛型
    public static <T> Retryer<T> build() {
        return new Retryer<T>();
    }
        
    // 入参泛型,返回值非泛型
    public <L, R> int sum(L[] left, R[] right) {
        return left.length + right.length;
    }

    // 入参泛型,返回值泛型
    public <L, R, V> Retryer<V> buildLength(L[] left, R[] right) {
        return new Retryer<V>(left.length + right.length);
    }
}

(2)List<T>是具体的返回值类型
(3)方法传参:可以用占位符限定的容器 比如 List<T>,或者直接是占位符 T 

2.类或者接口使用泛型
类使用泛型:class Box<T>{...}
接口使用泛型:interface List<E>{...}
以上,在jdk常见方法里,经常会看到使用了泛型,比如HashMap,List,ArrayList等,在实际项目开发中,一些基础的公共类经常定义泛型。
举个自定义的接口类:

public class GenericTest {
 
    public static void main(String[] args) {

         Box<String> name = new Box<String>("corn");
         System.out.println("name:" + name.getData());
    }

}

class Box<T> {

    private T data;

    public Box() {

    }

    public Box(T data) {
        this.data = data;
    }

    public T getData() {
        return data;
    }

} 

那么在实际的传入不同参数之后,生成的相应对象实例的类型是不是一样的呢?

public class GenericTest {

    public static void main(String[] args) {

        Box<String> name = new Box<String>("corn");
        Box<Integer> age = new Box<Integer>(712);

        System.out.println("name class:" + name.getClass());      // com.qqyumidi.Box
        System.out.println("age class:" + age.getClass());        // com.qqyumidi.Box
        System.out.println(name.getClass() == age.getClass());    // true

    }

}

由此,我们发现,在使用泛型类时,虽然传入了不同的泛型实参,但并没有真正意义上生成不同的类型,传入不同泛型实参的泛型类在内存上只有一个,即还是原来的最基本的类型(本实例中为Box),当然,在逻辑上我们可以理解成多个不同的泛型类型。
类型擦除
在JAVA的虚拟机中并不存在泛型,泛型只是为了完善java体系,增加程序员编程的便捷性以及安全性而创建的一种机制,在JAVA虚拟机中对应泛型的都是确定的类型,在编写泛型代码后,泛型只在编译器才进行检查,编译器会把这些泛型参数类型都擦除(即编译后,在查看它的class文件时,发现class文件并没有任何泛型信息),用相应的确定类型来代替,代替的这一动作叫做类型擦除,而用于替代的类型称为原始类型,在类型擦除过程中,一般使用第一个限定的类型来替换,若无限定,则使用Object.

3.限定边界
泛型的界限,其目的是在一定范围内扩大可以操作的参数类型。分为上界限,和下界限。
泛型上界:表现形式:<? extends A> 表示的含义是:可以操作的参数类型可以是,A类型以及A的子类。通常这种在集合中取出元素的时候常常使用,通常用A类型来接收取出的元素。<=A

泛型下界:表现形式:<? super A> 表示的含义是:可以操作的参数类型是:A类型及其父类型。通常这种在接收元素做比较的时候使用,可以使用自己的比较器,也可以使用父类的比较器。泛型下限的使用不多 。>=A

4.通配符的使用
泛型通配符:?表示,通常用于操作任意参数类型的泛型。强调的是通用性。

四、补充
1.异常中使用泛型
不能抛出也不能捕获泛型类的对象。事实上,泛型类扩展Throwable都不合法,因为泛型信息会被擦除,相当于catch两个相同的异常,是不可以的
2.数组与泛型
不能声明参数化类型的数组, 数组可以记住自己的元素类型,不能建立一个泛型数组。(当然 你如果用反射还是可以创建的,用Array.newInstance。这里说不能建是不能用普通方法)

public class TestArray {
    // java不支持创建泛型数组
    // http://blog.csdn.net/orzlzro/article/details/7017435
    public static <T> T[] test2(int length, Class<T> newType) {
        // T t = new T(); 编译错误
        // T[] wrong = new T[length]; 编译错误
        // T[] newArray = (T[]) new Object[length]; 运行时ClassCastException
 
        // 参考Arrays.copyOf
        T[] newArray = (T[]) Array.newInstance(newType, length);
        return newArray;
    }
}

3.基本类型无法作为类型参数即ArrayList<int>这样的代码是不允许的,如果为我们想要使用必须使用基本类型对应的包装器类型ArrayList<Integer>
4.在泛型代码内部,无法获得任何有关泛型参数类型的信息换句话说,如果传入的类型参数为T,即你在泛型代码内部你不知道T有什么方法,属性,关于T的一切信息都丢失了。
5.注意,在能够使用泛型方法的时候,尽量避免使整个类泛化。

以上重点阐述的是为什么使用泛型,泛型的概念,其基本的用法以及常见的一些说法,实际的例子比较少,这个在于自己的实践,毕竟实践才是理解泛型的唯一标准。有不到之处还请指正,欢迎交流。

参考:
http://www.cnblogs.com/lwbqqyumidi/p/3837629.html
https://www.cnblogs.com/ldh-better/p/7127308.html


 

猜你喜欢

转载自blog.csdn.net/u013521882/article/details/85263295