目录
什么是泛型?
所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值及参数类型。这个类型参数将在使用时(例如继承或实现接口,用这个类型声明变量、创建对象时)确定(既传入实际的类型参数,也称为类型实参)。
JDK1.5改写了集合框架中的全部接口和类,为这些接口、类增加了泛型支持,从而可以在声明集合变量、创建集合对象时传入类型参数。(List<String> 表明该list只能保存字符串类型的对象)
如果没有指定泛型的类型,默认类型为java.lang.Object类型。
为什么使用泛型?
- 集合中没有泛型之前任何元素都可以添加到集合中,会出现类型不安全。
- 强制转换时会出现ClassCastException
ArrayList list=new ArrayList();
list.add(10);
list.add(20);
list.add(30);
list.add(40);
list.add("awsl"); //类型不安全
for (Object object : list) {
int number = (int) object; //异常
System.out.println(number);
}
集合中的泛型使用
ArrayList<Integer> list=new ArrayList<>();
list.add(10);
list.add(20);
list.add(30);
list.add(40);
for (int number : list) {
System.out.println(number);
}
Map<String, Integer> map=new HashMap<String, Integer>();
map.put("Tom", 10);
map.put("Jerry", 20);
map.put("Mark", 30);
//泛型嵌套
Set<Map.Entry<String, Integer>> entry=map.entrySet();
Iterator<Map.Entry<String, Integer>> iterator=entry.iterator();
while (iterator.hasNext()) {
Map.Entry<String, Integer> e=iterator.next();
String key=e.getKey();
int number=e.getValue();
System.out.println(key+": "+number);
}
自定义泛型类
public class Study {
static class Pair<T>{
private T first;
private T second;
public Pair() { this.first=null; this.second=null; }
public Pair(T first,T second) { this.first=first; this.second=second; }
public T getFirst() { return first; }
public T getSecond() { return second; }
public void setFirst(T first) { this.first = first; }
public void setSecond(T second) { this.second = second; }
}
public static Pair<String> minmax(String[] a) {
if(a==null||a.length==0) return null;
String min=a[0];
String max=a[0];
for(int i=1;i<a.length;i++) {
if(min.compareTo(a[i])>0) min=a[i];
if(max.compareTo(a[i])<0) max=a[i];
}
return new Pair<>(min,max);
}
public static void main(String[] args) {
String[] word= {"Mary","had","a","little","lamb"};
Pair<String> mm=minmax(word);
System.out.println("min= "+mm.getFirst());
System.out.println("max= "+mm.getSecond());
}
}
运行结果:
min= Mary
max= little
当子类在继承带泛型的父类时,由于父类指定了泛型类型,则实例化子类对象时,子类不需要在声明泛型。
public class Sub extends Parent<Integer>{}
Sub sub=new Sub();
但是子类也可以定义为泛型:
public class Sub<T> extends Parent<T>{}
Sub<String> sub=new Sub<>();
泛型方法
泛型方法可以定义在普通类中,也可以定义在泛型类中(泛型方法的泛型参数与类的泛型参数没有任何关系)。
注意:public T getFirst() { return first; } 这些不是泛型方法。
public class Test<T>{
public <E> List<E> copyFromArrayToList(E[] array) { //注意返回值前面有一个类型参数
ArrayList<E> list = new ArrayList<E>();
for(E e : array) {
list.add(e);
}
return list;
}
public static void main(String[] args) {
Test<String> test=new Test<>();
Integer[] array = new Integer[] {1,2,3,4};
List<Integer> list = test.copyFromArrayToList(array);
System.out.println(list);
}
}
运行结果:[1, 2, 3, 4]
泛型方法可以声明为 static 静态的,原因:泛型参数是在调用方法时确定的,并非在实例化类时确定的。
有时,类或方法需要对类型变量加以约束,可以通过对类型变量E设置限定(bound)实现这一点:public <E> List<E extends Comparab1e> copyFromArrayToList(E[] array)
泛型的使用场景
假如我们需要对数据库进行操作,那么我们有一个DAO类来存放数据库的基本的增删改查操作。
public class DAO<T> {
//添加一条记录
public void add(T t) {...}
//删除一条记录
public boolean remove(T t) {...}
//修改一条记录
public void update(int index,T t) {...}
//查询一条记录
public T search(int index) {...}
}
同时,我们有一个客户类Customer
public class Customer {
String name;
int number;
public Customer () { this.name=null; this.number=null; }
public Customer (String name,int number) { this.name=name; this.number=number; }
public String getName() { return name; }
public int getNumber() { return number; }
public void setName(String name) { this.name= name; }
public void setNumber(int number) { this.number= number; }
}
我们还有一个专门对客户表进行操作的类
public class CustomerDAO extends DAO<Customer> {...}
现在,我们可以在其他地方对客户表操作
public class Test {
CustomerDAO dao = new CustomerDAO();
dao.add(new Cumstomer());
List<Customer> list = dao.search(10);
}
泛型在继承方面的体现
如果类A是类B的父类,G<A> 和 G<B> 二者不具备子父类关系,二者是并列关系。
Object object = null;
String stirng = null;
object = string; //正确
List<Object> list1 = null;
List<String> list2 = null;
list1 = list2; //错误
相反,如果类A是类B的父类,A<G> 是 B<G> 的父类。
List<String> list1 = null;
ArrayList<String> list2 = null;
list1 = list2; //子父类关系
无边界通配符:<?>
无边界的通配符的主要作用就是让泛型能够接受未知类型的数据。
public class Test<T>{
public static void print(List<?> list) {
//对于List<?>不能添加数据(除了null)
//Object是类的父类,并且不会报错
for (Object object : list) {
System.out.print(object);
}
}
public static void main(String[] args) {
List<Integer> list1 = Arrays.asList(new Integer[] {1,2,3,4});
List<String> list2 = Arrays.asList(new String[] {"Tom","Jerry","Marry","Tong"});
System.out.println(list1);
System.out.println(list2);
}
}
上界通配符:<? extends E>
在类型参数中使用 extends 表示这个泛型中的参数必须是 E 或者 E 的子类。
G<? extends A> 可以作为 G<A> 和G<B>的父类,其中B是A的子类 。
下界通配符:<? super E>
在类型参数中使用 super 表示这个泛型中的参数必须是 E 或者 E 的父类。
G<? super A> 可以作为 G<A> 和G<B>的父类,其中B是A的父类 。
泛型的约束与局限性
- 不能用基本类型实例化类型参数。
不能用类型参数代替基本类型。因此,没有Pair<double>, 只有Pair<Double> 。其原因是类型擦除。擦除之后,Pair类含有Object类型的域,而Object不能存储double值。 - 运行时类型查询只适用于原始类型
虚拟机中的对象总有一个特定的非泛型类型。因此,所有的类型查询只产生原始类型。试图查询一个对象是否属于某个泛型类型时,倘若使用instanceof会得到一个编译器错误,如果使用强制类型转换会得到一个警告。
if (a instanceof Pair<String>) // Error
if (a instanceof Pair<T>) // Error
同样的道理,getClass方法总是返回原始类型。
Pair<String> stringPair = . . .
Pair<Employee> employeePair = . . .
if (stringPair.getClass() == employeePair.getClass()) // they are equal
其比较的结果是 true, 这是因为两次调用 getClass 都将返回 Pair.class。 - 不能创建参数化类型的数组,Java不支持泛型类型的数组。
Pair<String>[] table = new Pair<String>[10]; // Error
擦除之后,table的类型是Pair[],可以把它转换为 Object[]:
Object[] objarray = table; - 不能实例化类型变量。不能构造泛型数组。不能使用像new T(…),new T[…]或T.class这样的表达式中的类型变量。
- 不能在静态域或静态方法中引用类型变量。禁止使用带有类型变量的静态域和静态方法。
public class Singleton<T>{
private static T singlelnstance; // Error
public static T getSinglelnstance(){ // Error
if (singleinstance == null)
construct new instance of T
return singlelnstance;
}
} - 不能抛出或捕获泛型类的实例。泛型类不能继承异常类。