Java泛型

来源:互联网 发布:mysql datetime 减法 编辑:程序博客网 时间:2024/05/20 18:01

1、为什么需要泛型

package com.xiya.generic;import java.util.ArrayList;import java.util.List;public class GenericTest {    public static void main(String[] args) {        List list = new ArrayList();        list.add("abc");        list.add(111);//可以通过编译        for (Object o : list) {            String value = (String) o;//抛出ClassCastException异常(java.lang.Integer cannot be cast to java.lang.String)            System.out.println(value);        }    }}

如上,我们向List类型集合中加入了一个String类型的值和一个Integer类型的值(这样是合法的,因为此时list默认的存储类型为Object类型)。
List add(Object o)
上面的代码会在运行时抛出ClassCastException异常,因为它尝试将一个Integer转换为String java.lang.Integer cannot be cast to java.lang.String。接着,来看一下从java5开始,Collection的用法:

package com.xiya.generic;import java.util.ArrayList;import java.util.List;public class GenericTest {    public static void main(String[] args) {        List<String> list = new ArrayList<>();        list.add("abc");        list.add("def");        //list.add(111);//不可以通过编译        for (String s : list) {            System.out.println(s);//无需任何强制类型转换        }    }}

注意到,List的创建增加了类型参数String,因此只能向List中添加String类型对象,添加其他对象会抛出编译异常;
同样可以注意到,foreach循环不需要再添加任何强制类型转换,也就移除了运行时的ClassCastException异常。
泛型的两个好处:
- 编译时类型检查。
- 避免强制类型转换。


2、泛型类与泛型接口

考虑以下场景:您希望开发一个用于在应用中传递对象的容器。但对象类型并不总是相同。因此,需要开发一个能够存储各种类型对象的容器。
鉴于这种情况,要实现此目标,显然最好的办法是开发一个能够存储和检索 Object 类型本身的容器,然后在将该对象用于各种类型时进行类型转换。

package com.xiya.generic;class ObjectContainer {    private Object obj;    /**     * @return the obj     */    public Object getObj() {        return obj;    }    /**     * @param obj the obj to set     */    public void setObj(Object obj) {        this.obj = obj;    }}public class GenericTest {    public static void main(String[] args) {        ObjectContainer objectContainer = new ObjectContainer();        objectContainer.setObj("Hello World");        System.out.println(objectContainer.getObj());    }}

原始类的定义,容易引发ClassCastException。
现在来看一下泛型类来重新定义ObjectContainer使用<T>指定泛型参数,如下:

package com.xiya.generic;class ObjectContainer<T> {    private T obj;    /**     * @return the obj     */    public T getObj() {        return obj;    }    /**     * @param obj the obj to set     */    public void setObj(T obj) {        this.obj = obj;    }}public class GenericTest {    public static void main(String[] args) {        ObjectContainer<String> objectContainer = new ObjectContainer<>();        objectContainer.setObj("Hello World");        //objectContainer.setObj(111);//无法通过编译        String str = objectContainer.getObj();        System.out.println(str);    }}

接口的泛型应用和类的泛型应用很类似,如下:

public interface List <E> {     void add(E x);     Iterator<E> iterator();}public interface Iterator<E> {     E next();     boolean hasNext();}

类似的,可以将此应用到自定义的接口与类当中。


3、泛型的命名规范

为了更好地去理解泛型,我们也需要去理解java泛型的命名规范。为了与java关键字区别开来,java泛型参数只是使用一个大写字母来定义。各种常用泛型参数的意义如下:
E — Element,常用在java Collection里,如:List< E >,Iterator< E >,Set< E >
K,V — Key,Value,代表Map的键值对
N — Number,数字
T — Type,类型,如String,Integer等等
S,U,V etc. - 2nd, 3rd, 4th 类型,和T的用法一样


4、泛型方法

定义泛型方法语法格式如下:
声明泛型方法

调用泛型方法语法格式如下:

public class GenericTest {    public static <T> T getObject(Class<T> c) throws IllegalAccessException, InstantiationException {        return c.newInstance();    }    public static void main(String[] args) {        try {            Person person = getObject(Person.class);            person.setName("lgh");            person.setAge(25);            System.out.println(person);        } catch (IllegalAccessException | InstantiationException e) {            e.printStackTrace();        }    }}

<T>T的区别?
其实说白了就是先声明后使用,和变量的使用没有本质区别。


5、泛型参数的界限

有时候,你会希望泛型类型只能是某一部分类型,比如操作数据的时候,你会希望是Number或其子类类型。这个想法其实就是给泛型参数添加一个界限。其定义形式为:
<T extends BoundingType>
此定义表示T应该是BoundingType的子类型(subtype)。T和BoundingType可以是类,也可以是接口。另外注意的是,此处的extends表示的子类型,不等同于继承。

public class GenericTest {    public static <T extends Number> void test(List<T> list) {        System.out.println(list);    }    public static void main(String[] args) {        List<Integer> list1 = Arrays.asList(1, 2, 3, 4, 5);        List<Number> list2 = Arrays.asList(6, 7, 8, 9, 10);        List<String> list3 = Arrays.asList("111", "222");        test(list1);        test(list2);        //test(list3);//报错    }}

此外,泛型只在编译阶段有效。运行时会进行类型擦除。
List<String> list = new ArrayList<>(); 编译后变成
List list = new ArrayList<>();

package com.xiya.generic;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.ArrayList;import java.util.List;public class GenericTest {    public static void main(String[] args) {        List<String> list = new ArrayList<>();        list.add("111");        list.add("222");//        list.add(333);        try {            Method method = list.getClass().getMethod("add", Object.class);            method.invoke(list, 333);        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {            e.printStackTrace();        }        System.out.println(list);    }}

参考:
http://peiquan.blog.51cto.com/7518552/1302898
https://www.ziwenxie.site/2017/03/01/java-generic/
http://www.oracle.com/technetwork/cn/articles/java/juneau-generics-2255374-zhs.html
http://www.infoq.com/cn/articles/cf-java-generics
https://www.ibm.com/developerworks/cn/java/j-lo-gj/index.html
http://oldratlee.com/278/tech/java/study-material-of-java-generic.html
http://blog.csdn.net/LonelyRoamer/article/category/1212099