泛型

来源:互联网 发布:mac开机黑屏有进度条 编辑:程序博客网 时间:2024/05/16 10:44

集合可以存储任何类型的对象,但是当把一个对象存入集合后,集合会“忘记”这个对象的类型,将该对象从集合中取出时,这个对象的编译类型就变成了Object类型。换句话说,在程序中无法确定一个集合中的元素到底是什么类型的。那么在取出元素时,如果进行强制类型转换就很容易出错。

package com.first;import java.util.ArrayList;public class HelloWorld {    public static void main(String[] args) {        ArrayList list=new ArrayList();        list.add("a"); //添加字符串对象        list.add("b");        list.add(1);  //添加Integer对象        for (Object obj : list) {            String str=(String)obj;//强制转换成String类型        }    }}

这时候会报类型转换的异常,java.lang.ClassCastException。

当试图将对象强制转换为不是实例的子类时,抛出该异常。以下代码会报同样的错。

Object x = new Integer(0);System.out.println((String)x);

为了解决这个问题,在Java中引入了“参数化类型”这个概念,即泛型。它可以限定方法的数据类型,在定义集合类时,使用“<参数化类型>”的方式制定该类中方法操作的数据类型,具体格式如下:

ArrayList<参数化类型> list=newArrayList<参数化类型>();

前后的泛型必须一致,或者后面的泛型可以省略不写(1.7的新特性,菱形泛型)。

泛型的好处:

  • 提高安全性(将运行期的错误转换到编译期)
  • 省去强转的麻烦

自定义泛型

假设要实现一个简单的容器,用于缓存程序中的某个值,此时在这个容器类中势必要定义两个方法save()和get(),一个用于保存数据,另一个用于取出数据。

为了能存储任意类型的对象,save()方法的参数需要定义为Object类型,同样get()方法的返回值也需要是Object类型。但是当使用get()方法取出这个值时,有可能忘记当初存储的是什么类型的值,在取出时将其转换为String类型,这样程序就会发生错误。

package com.first;public class HelloWorld {    public static void main(String[] args) {        CachePool pool=new CachePool();        pool.save(1);        String temp=(String)pool.get();        System.out.println(temp);    }}class CachePool{    Object temp;    public void save(Object temp){        this.temp=temp;    }    public Object get(){        return temp;    }}

程序会报类型转换错误。

为了避免这个问题,就可以使用泛型。如果在定义一个类CachePool时使用<T>声明参数类型(T其实就是Type的缩写,这里也可以使用其他字符,为了方便理解都定义为T),将save()方法的参数类型和get()方法的返回值类型都声明为T,那么在存入元素时元素的类型就被限定了,容器中就只能存入这种T类型的元素,在取出元素时就无须进行类型转换。

package com.first;public class HelloWorld {    public static void main(String[] args) {        CachePool<Integer> pool=new CachePool<Integer>();        pool.save(1);        int temp=pool.get();        System.out.println(temp);    }}class CachePool<T>{    T temp;    public void save(T temp){        this.temp=temp;    }    public T get(){        return temp;    }}

运行结果为1。

在定义CachePool类时,声明了参数类型为T,在实例化对象时通过将参数T指定为Integer,同时在调用save()方法时传入的数据也是Integer类型,那么调用get()方法取出的数据自然就是Integer类型,这样做的好处是不需要进行类型转换。

其中方法的泛型也可以和类的泛型不同,但建议相同。如果不同,需要在方法上声明该泛型。

如果是静态方法的话,就必须在方法上声明泛型了,因为类的泛型,是在实例化对象的时候才会把泛型传进来,而静态方法是随着类的加载而加载的,那时候还没有对象。

class CachePool<T>{    T temp;    public void save(T temp){        this.temp=temp;    }    public T get(){        return temp;    }    public<Q> void show(Q q){  //如果用T的话,就和类的泛型相同了        System.out.println(q);    }    public static<T> void print(T t){  //这个T和类上的T没有联系,为了防止歧义,也可以叫W        System.out.println(t);    }}

在接口上自定义泛型

interface Inter<T>{    public void show(T t);}class Demo implements Inter<String>{    @Override    public void show(String t) {        System.out.println(t);    }}

通配符

<?>代表任意类型

List<?> list=new ArrayList<String>();

前后应该类型一致才可以,但开发中可能不确定后面会接受什么类型,这时候就可以用通配符<?>

注意这里不能写成Object,Object和String类型不一致,会报错。

boolean addAll(Collection<? extends E> c)这个方法中,参数Collection中存储的类型只能是E及其子类。

package com.first;import java.util.ArrayList;public class HelloWorld {    public static void main(String[] args) {        ArrayList<Person> list1=new ArrayList<Person>();        list1.add(new Person("张三",22));        list1.add(new Person("李四",23));        ArrayList<Student> list2=new ArrayList<Student>();        list2.add(new Student("王五",24));        list2.add(new Student("赵六",25));        list1.addAll(list2);        System.out.println(list1);    }}class Person{    String name;    int age;    public Person(String name, int age) {        this.name = name;        this.age = age;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    @Override    public String toString() {        return "Person [name=" + name + ", age=" + age + "]";    }}class Student extends Person {    public Student(String name, int age) {        super(name, age);    }    @Override    public String toString() {        return "Student [name=" + name + ", age=" + age + "]";    }}

list2中的Student是继承自list1中的Person类的,可以运行成功。

但如果写成list2.addAll(list1);就会报错,因为list1中元素的类型Person并不是list2中Student类的子类。

  • ? extends E:向下限定,E及其子类
  • ? super E:向上限定,E及其父类
0 0
原创粉丝点击