Java泛型

一 、为什么要使用泛型?

现在,有下面两个类:

class A{
	public void a() {
		//do something
	}
}

class B {
	public void b() {
		//do something
	}
}

如果我们需要一个ArrayList,用来装载A类对象,然后执行a方法,没有泛型的话我们需要这么做:

public static void main(String[] args) {
	ArrayList list = new ArrayList();
	list.add(new A());
	list.add(new A());
	list.add(new A());
	list.add(new B());//语句1
	
	for(A a: list) {
		a.a();	
	}

}

就算我们向List加入B类对象,编译器也不会报错,只有等到程序运行的时候,我们才知道,我们加入了不该加的东西。没有泛型,我们无法确认自己加进去的是什么东西,而此时也不会报错。但是如果使用泛型,那么我们就能保证加进去的东西是我们想要的。
如果上面那段代码使用泛型:

public static void main(String[] args) {
	ArrayList<A> list = new ArrayList<>();
	list.add(new A());
	list.add(new A());
	list.add(new A());
	list.add(new B());//语句1
	
	for(A a: list) {
		a.a();	
	}
}

如果这时候再向list添加B类的对象,那么编译器将会报错。

二 、泛型是如何工作的?
  1. 泛型一个重要的作用,就是保证了“类型安全”,即,当有不正确的类型加入的时候,编译器会报错。
  2. 泛型另一个重要的点,就是类型擦除:使用了泛型的代码的字节码,和没有使用泛型的字节码是一样的。

举个例子来解释一下:

List<Integer> list = new ArrayList<Integer>();
 
list.add(1000);     //works fine
 
//list.add("lokesh"); //语句1

类型安全,使得不是Integer的对象无法被加入。

List list = new ArrayList();
 
list.add(1000);

类型擦除,让这段代码的字节码和上面那段代码的字节码一样。泛型其实只是一个语法糖,它保证了类型安全,但是编译器在编译的时候,会把有关泛型的信息消除。

三、泛型的具体应用
1. 泛型类,接口

如果没有泛型,我们要实现一个可以根据不同场景加入不同对象的类,我们只能这样实现:

public class GenericDemo {
	Object obj;

	public Object get() {
		return obj;
	}

	public void set(Obj obj) {
		this.obj = obj;
	}
}

我们在get的时候,还得把Object转换为我们需要的类型,非常麻烦,且不安全。
有了泛型以后:

public class GenericDemo<T> {
	T obj;
	
	public T get() {
		return obj;
	}

	public void set(T obj) {
		this.obj = obj;
	}
} 

这样,我们就能任意指定自己所需要的类型。而且,泛型类,接口可以同时定义多个泛型,如下:

//Generic interface definition
interface DemoInterface<T1, T2>
{
   T2 doSomeOperation(T1 t);
   T1 doReverseOperation(T2 t);
}
 
//A class implementing generic interface
class DemoClass implements DemoInterface<String, Integer>
{
   public Integer doSomeOperation(String t)
   {
      //some code
   }
   public String doReverseOperation(Integer t)
   {
      //some code
   }
}
2. 泛型方法

现在有这么一个需求,判断一个list中有多少个和item相同的对象,但是item的类型不确定,那么要怎么做?这时候就可以使用泛型方法来解决。

public static <T> int countAllOccurrences(T[] list, T item) {
   int count = 0;
   if (item == null) {
      for ( T listItem : list )
         if (listItem == null)
            count++;
   }
   else {
      for ( T listItem : list )
         if (item.equals(listItem))
            count++;
   }
   return count;
}  
四、泛型通配符

泛型通配符的主要使用方式有三种:

  1. Collection<?>
  2. List<? extends Number>
  3. Comparator<? super String>

第一种,表示Collection的泛型类型可以是任何类。
第二种,表示List的泛型类型必须是Number的子类。
第三种,表示Comparator的泛型类型必须是String的父类。

例如,下面这三条语句,就是泛型通配符的正确使用方式:

Collection<?> coll = new ArrayList<String>();
//OR
List<? extends Number> list = new ArrayList<Long>();
//OR
Pair<String,?> pair = new Pair<String,Integer>();

而下面这两条语句,将会报错:

List<? extends Number> list = new ArrayList<String>();  //String is not subclass of Number; so error
//OR
Comparator<? super String> cmp = new RuleBasedCollator(new Integer(100)); //Integer is not superclass of String
五、什么情况下无法使用泛型
  1. 不能有static的泛型成员变量:
public class GenericDemo<T> {
	public static T t; //error
}

static变量是类成员变量,所有的对象共享。如果两个对象的具体类型不一样,那么这个static成员变量到底属于哪种?因此不能有static的泛型成员变量。

  1. 我们无法创建泛型实例对象:
public class GenericsExample<T>
{
   public GenericsExample(){
      new T();//error
   }
}
  1. 不能创建泛型的Exception
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值