Java高级特性–泛型

泛型是java5新增的特性之一,在某种程度上能增加代码的灵活性。它的作用在于对类型进行抽象,废话少说,先看个例子。 在没有泛型之前,如果我们要写一段代码,针对不同的基本数据类型(String,IntegerDouble,Float等),如:

public class StringObject {
		private String str;
		public void setString(String str) {
			this.str=str;
		}
		public String getString() {
			return str;
		}
	}

如果还有其他的基本数字类型,需要些写同样的类。当然,还有一种方法,就是将所有的基本类型抽象成Object,如下:

package generics;
	public class ObjectTest {
		private Object x;
		public void setX(Object x) {
			this.x=x;
		}
		public Object getX() {
			return x;
		}
	}
package generics;
	public class ObjectTestDemo {
 
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ObjectTest strFoo=new ObjectTest();
		strFoo.setX("strFoo");
 
		ObjectTest intFoo=new ObjectTest();
		intFoo.setX(new Integer(11));
 
		ObjectTest douFoo=new ObjectTest();
		douFoo.setX(new Double(1.33));
 
		String str=(String)strFoo.getX();
		System.out.println(str);
		Integer in = (Integer)intFoo.getX();
		System.out.println(in);
		Double dou = (Double)douFoo.getX();
		System.out.println(dou);
		}
 
	}


但是他有一个缺点,就是需要做强制类型转换,将Object向下转型为基本类型。
自java 5引入泛型概念后,对付这种问题,就简单了很多。

package generics;
	public class GenericsTest<T> {
		private T x;
		public void setX(T x) {
		this.x=x;
		}
 
		public T getX() {
			return x;
		}
	}
package generics;
	public class GenericsTestDemo {
 
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		GenericsTest<String> str=new GenericsTest<String>();
		str.setX("Generics");
 
		GenericsTest<Double> dou=new GenericsTest<Double>();
		dou.setX(1.33);//自动装箱
 
		GenericsTest<Integer> in=new GenericsTest<Integer>();
		in.setX(2);//自动装箱
 
		String strDemo = str.getX();
		System.out.println(strDemo);
		Double douDemo = dou.getX();
		System.out.println(douDemo);
		Integer inDemo = in.getX();
		System.out.println(inDemo);//自动拆箱
		}
 
	}

在GenericisTest中,没有明确定义具体是哪种类型,而是以代替需要的类型。在代码上隐去了类型,在实际运用的时候,加入了类型的限制。他的优点不仅仅是去掉了向下转型的困难,而且,泛型是在编译时检查的,可以避免将错误带入运行时,最终的结果是,泛型增强了代码的可读性和稳定性。
1.泛型用在接口上:

public interface Test<A,B> {
		public A getA();
		public B getB();
		public String toString();
	}

A B代表类型,放在<>中
2.泛型用在类上:

package generics;
	public class GenericsTest<T> {
		private T x;
		public void setX(T x) {
		this.x=x;
		}
 
		public T getX() {
			return x;
		}
	}

如果某个类实现了泛型接口,写法如下:

public class ClassTest<A,B> implements Test<A,B>

3.泛型用在方法上:

public class GenericMethod {
		public <T> void print(T v) {
			System.out.println(v.toString);
		}
	}

以上是简单的泛型应用,在实际编码中,会有不同的限制
4.泛型传入的类型是某个类的子类

<T extends BaseClass>

如果泛型T继承了多个接口,语法如下:

<T extends BaseeClass&BaseInterface2&BaseInterFace3>

注意:类(BaseClass)必须在接口(BaseInterFace2)前面

package generics;
	public class BaseClass {
		private int x;
		public BaseClass(int x) {
			this.x=x;
		}
		public int getX() {
			return x;
		}
		public void setX(int x) {
			this.x=x;
		}
	}
package generics;
	public class DeriveClass extends BaseClass {
 
	public DeriveClass(int x) {
		super(x+5);
		// TODO Auto-generated constructor stub
		}
	} 
	<pre lang="java">package generics;
 
	import java.util.LinkedList;
	import java.util.List;
 
	public class GenericDemo<T extends BaseClass> {
 
	/**
	 * @param args
	 */
	public int sum(List<T> first) {
		int i=0;
		for(BaseClass base:first) {
			i+=base.getX();
		}
		return i;
	}
 
	public static void main(String[] args) {
		GenericDemo<DeriveClass> gd=new GenericDemo<DeriveClass>();
		List<DeriveClass> second=new LinkedList<DeriveClass>();
		//List<BaseClass> third=new LinkedList<DeriveClass>(); 不能向上转型,没有这样的概念,是不同的类型
		second.add(new DeriveClass(1));
		second.add(new DeriveClass(2));
		second.add(new DeriveClass(3));
		int s=gd.sum(second);
		System.out.println(s);
		}
	}

输出结果为21.
注意其中注释的语句:

List<BaseClass> third=new LinkedList<DeriveClass>();

泛型类型(DeriveClass)继承(BaseClass),并不代表LinkedList()是List的子类。这一点是由于泛型的擦出机制,将在后面介绍
5.通配符泛型
先看个示例:

package generics;
 
	import java.util.ArrayList;
	import java.util.LinkedList;
	import java.util.List;
 
	public class WildGeneric {
 
	/**
	 * @param args
	 */
	public void print(List<?> first) {
		for(int i=0;i<first.size();i++) {
			System.out.println(first.get(i));
		}
	}
 
	public void println(List<? extends Number> second) {
		for(Number num: second) {
			System.out.println(num);
		}
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		WildGeneric wg=new WildGeneric();
 
		List<String> str=new ArrayList<String>();
		str.add("the first line");
		str.add("the second line");
		str.add("the third line");
		str.add("the fourth line");
		wg.print(str);
 
		List<Integer> in=new LinkedList<Integer>();
		in.add(1);
		in.add(2);
		in.add(3);
		in.add(4);
		wg.println(in);
		}
 
	}

List first 读音:List of unknow,就是List的元素类型可以匹配任何类型。代码中还有另一种形式:List,extends为上边界符,同理,还有下边界符super:List
6.擦出机制

GenericsTest<?> gt1=new GenericsTest<?>();
	gt1.setX("test");

这行代码会报错,因为不知道是何种类型,所以也无法将对象添加进去。
运行以上的代码,会发现gts与gt2相比较的结果是true,这就是说,gts与gt2属于同一种类型。就是说在jdk内部,实行了擦出机制,运行时没有任何与泛型相关的信息。这也就解释了List first的原因,他被擦出为List first失去了类型信息。
在实际运用中,不需要深究擦出机制,只要能运用就可以。了解擦出机制,有利于我们深刻的理解泛型丢失的原因。

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注

*

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">

Go back to top