java泛型——基本使用

泛型

因为泛型使用较多的场景是在集合中,我们以集合为例来说说泛型。java集合的一个缺点就是,我们放入一个东西之后,他并不知道这个东西的数据类型。如何理解?看下面的代码。

        List list=new ArrayList();
        list.add("Sherry");
        list.add(18);

这段代码编译、运行是没有任何问题的,但会报警告(下面会介绍)。现在将两个元素放入集合list中,当往外读的时候就有问题了,因为集合并不知道哪个对象具体是什么数据类型的。

当我们从集合中读数据时,因为不知道具体的数据类型,经常会遇到类型转换的问题,比如:

        //输出
        for (int i = 0; i < list.size(); i++) {
            System.out.println((String)list.get(i));
        }

报错信息:

Exception in thread “main” java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at com.ysx.test.GenericTest.main(GenericTest.java:16)


如何解决

这里写图片描述

上图将不同类型的元素放入各自的集合中,这时我们进行读写操作时就不需要考虑是否需要类型转换,如何转,是否可以转换成功等问题了。

解决的思路很简单,就是将类型抽象出来作为参数。在定义集合的时候就说明是用来存放何种类型的。

将上面的例子改造下,改造之后代码就不报警告了。

        List<String> listStr=new ArrayList<String>();
        listStr.add("Sherry");
        listStr.add("123");
        for (int i = 0; i < listStr.size(); i++) {
            System.out.println(listStr.get(i));
        }

Tips:

因为List<String> listStr=new ArrayList<String>();中ArrayList后面的String显得有些多余,java7之后就不需要写完整信息了。写成List<String> listStr=new ArrayList<>();就可以了。

书上的定义:

泛型,就是允许在定义类、接口、方法时使用类型形参,这个形参将在声明变量、创建对象、调用方法时动态的指定。

回顾下List的接口定义:

public interface List<E> extends Collection<E> {
}

在定义接口的时候指定了类型形参E,因此,我们在使用的时候才可以直接在List后面加参数类型。
Collection和Map可以说是集合的父接口,其他都是对他们的继承或者实现,我们可以说java集合对支持泛型提供了良好的基础。

当然,我们可以不需要把泛型加在类或接口上,而是直接加到某个方法上,即泛型方法。

泛型方法

先看个例子:

public class GenericMethodTest {
    //泛型方法
    public static <T> void Test(List<T> c) {
        for (int i = 0; i < c.size(); i++) {
            System.out.println(c.get(i));
        }
    }

    public static void main(String[] args) {
        // List<Object>
        List<Object> obj = new ArrayList<>();
        obj.add(2);
        obj.add("dd");
        Test(obj);

        // List<Stromg>
        List<String> strList = new ArrayList<>();
        strList.add("a");
        strList.add("b");
        Test(strList);
    }
}

在该泛型方法中,使用了T类型形参,他的位置应该在修饰符(public、static、final等)之后,在返回值之前。

泛型方法和泛型类区别归根到底是方法和类,比如:作用域等。

泛型方法还有一个好朋友——类型通配符,他们经常被拿来作比较。

类型通配符

当我们使用一个泛型类或方法时,如果不传入实际类型参数,就会给出黄色警告。
比如:

public void test(List c){
    for(int i=0;i<c.size();i++){
        System.out.println(c.get(i));
    }
}

在例子中,List c 并没有指定具体的类型,这个集合中需要放入的类型并不确定,我们无法加上任意一种形式。这时,使用通配符“?”就可以避免这个问题。

修改一下代码:

public void test(List<?> c){
    for(int i=0;i<c.size();i++){
        System.out.println(c.get(i));
    }
}

使用类型通配符之后,警告就不见了。但因为集合中的类型是不确定的,我们只可以进行简单的查询操作,没办法添加元素,除了添加null情况。

有人会问了,那这通配符有什么用啊,集合的常用操作都实现不了,加不加有什么用啊?别急,他的精髓还在后面……

设定上限

List<?>代表元素类型未知的List集合,他的元素可以匹配任何类型。更多时候,我们希望他只匹配某一类泛型的父类。

我们以MiniCat-》Cat-》Animal为例,三者之间是继承关系。

   public static void test3(List<? extends Cat> cats) {
        for (Cat c : cats) {
            System.out.println(c.getName());
        }
    }

下面我们开始调用,上面例子中的意思是Cat类以及其子类可以放入List集合中,我们来试一下他的父类Animal。

public static void main(String[] args) {

        List<Animal> a = new ArrayList<Animal>();
        a.add(new Animal("pig", "white"));
        a.add(new Animal("apple", "white"));
        test3(a);

    }

此时,编译报错,报错信息为:

这里写图片描述

之后,将Animal改为Cat类,顺利执行。

下限

下限的设定使用<? super Class>

public static void test4(List<? super Cat> cats) {
        for (Object obj: cats) {
            Cat c = (Cat) obj;
            System.out.println(c.getName());
        }
    }

这代表下限是Cat类,满足条件的是Cat的父类,这时如果传入MiniCat类,仍旧会报上面的错。


比较

大多数情况下都可以使用泛型方法来代替类型通配符。

通配符支持灵活的子类化。

若方法中的参数或返回值之间有类型依赖关系,应该使用泛型方法。

有时候,他们两者也结合使用。

posted @ 2016-04-19 14:59  Sherry&Yang  阅读(196)  评论(0编辑  收藏  举报