首页 > 技术文章 > java泛型

zhuxiang1633 2019-07-26 13:30 原文

为什么要用泛型?

Java有个缺点:当我们把一个对象丢进集合中后,集合就会忘记这个对象的数据类型,把他们当成Object类处理,当再次取出这个对象时,需要强制转化数据类型。

Java集合之所以被设计成这样,是因为集合的程序员不会知道我们需要用它来保存什么类型的对象,所以把他们设计成能保存任何类型的对象,只要求很好的通用性。但是存在两个问题:

l 例如想创建一个只能保存Dog类型的集合,但是程序也可以添加Cat对象进去。

l 给取出元素造成不必要的麻烦。

使用泛型的好处:可以写出更加通用的代码,帮我们检查语法(泛型只在编译阶段有效)。

在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

  • demo

public class TestgenericList {
public static void main(String[] args) {
List<String> l=new ArrayList<String>();
l.add("bob");
l.add("张三");
l.add("李四");
//下面的代码将引起编译错误
// l.add(5);
for(String str:l){
//无需强制转化
String name=str;
System.out.println(name);
}
}
}

定义泛型接口、泛型类

  • List接口

public interfere List<E>
{
 //在该接口中,E可以作为类型使用
 //下面方法可以使用E作为参数类型。
 void add(E e);
 Iterator<E> iterator();
}

不仅集合可以增加泛型,任何类都可以增加,虽然泛型是集合的重要场合。下面自定义了一个Apple类,这个类就可以包含一个泛型声明

public class Apple<T> {
private T info;
public Apple(){}
public Apple(T info){
this.info=info;
}
  ...
}

 

并不存在的泛型类

  • 直接看代码

List<String> l1=new ArrayList<String>();
List< Integer > l2=new ArrayList<Integer>();
System.out.println(l1.getClass()==l2.getClass());   // ? true

不可以这样判断:if(cs instanceof List<String>)

通配符(表示各种泛型List的父类)

问题:形参的类型需要使用通用类型。

public void fun(Generic<Number> obj){}

Generic<Integer> gInteger = new Generic<Integer>(123);
fun(gInteger);

这段代码会报错

public void test( List<?> l ) { }
// 不管List泛型是什么类型,都被当成了Object类处理

通配符的上下限

class Apple<T extends Number>{  // Number为T的上限,T不能是Number的父类,可以是Number类型
public T info;
public String toString(){
return "info: "+this.info;
}
}
class Apple<T super Number>{  // Number为T的下限
public T info;
public String toString(){
return "info: "+this.info;
}
}

泛型方法

// T是方法的类型参数,可以根据实参的类型就行推断
public static <T> void test(T[]a,Collection<T> c){
   for(T t:a){
       c.add(t);
  }
}
public static void main(String[] args) {
   Collection<Object> cl=new ArrayList<Object>();
   Object []obj=new Object[3];
   obj[0]="zhangsan";
   obj[1]="lisi";
   obj[2]="bob";
   test(obj,cl);   // 根据object的实际类型推断出T类型
   System.out.println(cl);
   //
   String []str={"A","B","C"};
   Collection<String> cl1=new ArrayList<String>();
   //这时以Collection为基准
   test(str,cl);
   System.out.println(cl);
}

先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法

也可以定义多个泛型参数:public static <T,S> void fun(){}

限定上下限

<T> void test(Collection<? extends T> a,Collection<T> c){}

 

擦除和转换

在做类型转换的时候,如果去掉了类型参数,则会造成对象的类型参数丢失,编译器认为类型参数为Object

public static void main(String[] args) {
   Apple<Integer> a=new Apple<Integer>();
   //a的getSize方法返回一个Integer对象
   Integer in=a.getSize();
   //把a对象赋值给Apple对象,则会丢失尖括号的信息
   Apple a1=a;  
   //下面的代码将会导致编译错误
   // Integer in1=a1.getSize();
}

 

推荐阅读