泛型概述
数组与集合
- Java中可以定义任意类型的属性,例如String[]中存放的就是String类型的数据,我们称之为持有String类型的数组。但1.5之前时,Java的集合类却只能持有Object类型,1.5时添加了泛型的概念,泛型允许Java创建持有任意类型的集合对象,例如:
new ArrayList<String>()
表示这个ArrayList中只能持有String类型的对象。
类型变量(参数)
- 具有一个或多个类型参数的类就是泛型类。
泛型类都至少有一个类型变量,你需要在创建泛型类对象时给类型变量赋值。当然,你要给类型变量赋的值必须是一个类型!
ArrayList<String> arr = new ArrayList<String>();
- 其中String就是给ArrayList类的类型变量赋值。在ArrayList类中所有使用类型变量的地方都会被String所替换。例如:boolean
add(E e),其中e的类型就是变量,它会被String替换,最终变成boolean add(String e)。E get(int
index)方法中返回值的类型为变量,它也会被String替换,最终变成String get(int index)。
泛型的好处
- 将运行期遇到的问题转移到了编译期。例如在1.4时,ArrayList类的add()方法参数还是Object类型,当然get()方法的返回值类型也是Object。这就说明使用get()方法获返回值后,你还需要强转。错误的强转可能会出现ClassCastException。
ArrayList list = new ArrayList();
list.add(“hello”);
Integer i = (Integer)list.get(0);//抛出异常
这个问题在有了泛型之后就不会再有了。
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(“hello”);//编译出错!
Integer i = (Integer)list.get(0);
很明显,是泛型把只能在运行时才能找到的错误推向了编译期!这就是泛型的优点!!!
定义泛型类(接口)
自定义泛型类的语法
- 自定义泛型类的语法基本与定义正常的法一样:
public class A<T> {}
。定义泛型类时,需要在类名后面给出一对尖括号,在尖括号中给出1~N个类型变量。
泛型类中使用类型变量
- 用户在使用泛型类时,需要为类型变量赋值。例如:new A()。在泛型类中可以使用类型变量:
public class A<T> {
private T t;
public A(T t) {
this.t = t;
}
public T get() {
return t;
}
}
- 当用户创建A类对象时,就会给A类的类型变量T赋值,例如:
new A<String>()
,这说明在A类中所有的T都会被String替换。
public class A {
private String t;
public A(String t) {
this.t = t;
}
public String get() {
return t;
}
}
类型变量的限制
- 上例中,A类中定义了T类型的属性t,这个属性t的类型是一个变量,无法确定的,因为你在定义A类时,根本就不知道用户会传递什么类型给T,所以你也不能调用属性t的方法,因为你不知道t的类型,就不知道它有什么方法。但是,我们知道任何类都是Object类的子类,那么就说明你可以调用属性t的Object中存在的方法。
- 注意,你不能使用T类型的构造器:new T(),因为你不能确定t的类型,那么也就不知道它有什么样的构造器,甚至是否有public构造器,所以不能创建。
- 相同的道理,也不能创建T类型的属性,例如:new T[10],这也是不行的!还有一点,在泛型类中,static方法中不能使用类型变量T。
继承(实现)泛型类(接口)
继承泛型类之一
- 如果当前类是泛型类,那么在继承(实现)泛型类(接口)时,可以把自己的类型变量传递给父类(接口)。
public class ArrayList<E> implements List<E> { }
- 可以这样来理解,其中
public class ArrayList<E>
中的<E>
表示定义了一个泛型类,其中E是定义的类型变量,而implements List<E>
中的<E>
表示给List接口传递<E>
,这是在使用变量类型E,而不是在定义了。
ArrayList<String> arr = new ArrayList<String>();
- 在用户创建ArrayList类对象时,传递给ArrayList类中的E的值是String,那么ArrayList会把这个String再传递给List中的E。
- 你可能会想,为什么一定要给父类或接口传递类型变量。那我会问你,实现类不给List接口中的E来赋值,谁来给它赋值?难道你想
new List<String>()
这样赋值么?接口是不能被实例化的,你只能让实现类来赋值,你不赋值,接口中的get()方法一直返回E类型!!!
继承泛型类之二
- 泛型类的子类不一定必须为泛型类,也可以是非泛型类。这时因为子类没有类型变量可以传递给父类,那么也就能传递给父类类型常量了。
public class String implements Comparable<String> {
public int compareTo(String other) {…}
}
- 这时,再重写父类中方法时,所有的类型变量都是String了。