1、泛型由来
在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换。
Java泛型的目的主要是可以建立具有类型安全的数据结构,如链表,散列表。在使用泛型建立数据结构时不必进行强制转换。即不要求运行时进行类型检查。在编译 List<V>类时,编译器不知道 V所表示的类型.Jdk 1.5是支持泛型的编译器,将运行时的类型检查提前到编译的时候,是代码更安全。
/* 虽然在写法上不会确定它的实际类型,但是它既然要被使用,那么在用的时候就会确定, 所以,在编译时期自动检查正在用的类型. 但是这个时候我们会有疑问?,用Object有什么区别呢!, 我们都知道,一旦把子类的类型引用传给了Object,这样当然不会报错,但是Object此时具有的属性就会减少, 只会具有重写的以及自己特有的,那么如何重新具有子类的特有的呢? 呵呵!当然是强转了,既然得强转,那么就得事先把强转的括号加上, 但是Object可以接受多种对象的引用,不同的强转就会有异常! 而泛型一开始就帮你发现了这个转换异常!,因为你已经限制了实际类型了! */ class Unknow2<E>{ private E t; public void setT(E t){ this.t = t; } public E getT(){ return t; } } class Unknow1{ private Object t; public void setT(Object t){ this.t = t; } public Object getT(){ return t; } } public class Generic1 { public static void main(String args[]){ Unknow1 u = new Unknow1(); u.setT("1111String1111"); //没错 // u.setT(2); //但运行时CastException System.out.println(((String)u.getT()).length()); /* 编译时期的确不会报错,但是运行就会出现转换异常 java.lang.Integer cannot be cast to java.lang.String 也许我们又在这有疑问了,既然set Integer 会报错, 那我保证不传了,有什么了不起,切 ! 但是实际上你却是允许传的,一旦你传了,你却还以为你还是正确的,隐藏的 bug 那我们怎么做呢??好吧,泛型出现了,她会提前告诉你的哦,*/ // 在使用时就已经会把它 实际类型 确定,否则不会通过 Unknow2<String> t = new Unknow2<String>(); // t.setT(2); //编译时期就已经限定了,只能为String t.setT("2String2"); System.out.println(t.getT().length()); } }
2、泛型用法
这种参数化类型可以用在类,接口,方法,的创建中,分别称之为泛型类,泛型接口,泛型方法,引入泛型的好处是安全,透明!
import java.util.Arrays; import java.util.Comparator; interface A<E ,F >{ public void sing(E e,F f); } class B<E,F> implements A<E,F>{ public void sing(E e,F f) { // 暂时 ! 只能调用从object继承或重写的方法 System.out.print("generic Interface: "+e.toString()); System.out.println(f.toString()); } } class C1{ public String toString(){ return "good good study,"; } }class C2{ public String toString(){ return "day day up!"; } } class D<E>{ /* 泛型方法,除了定义不同,调用就像普通方法! * 一个static方法,无法使用泛型类的类型参数! 因为泛型类中的泛型参数的实例化是在定义对象的时候指定的, 而静态变量和静态方法不需要使用对象来调用。对象都没有创建, 如何确定这个泛型参数是何种类型,所以当然是错误的。 为使其具有泛型功能,必须指定为泛型方法 !*/ public void test1(E e){ System.out.print("generic class: "+e.toString()); } public static <E> void test2(E e){ System.out.println(e.toString()); } } public class Generic2 { public static void main(String args[]){ common(); example(); } private static void common() { A<C1,C2> a = new B<C1,C2>(); a.sing(new C1(), new C2()); D<C1> d = new D<C1>(); d.test1(new C1()); D.test2(new C2()); } private static void example() { String source = "12,33,45,456,1,34,235,345,9,90,355,552,221,11,45"; String temp[] = source.split(","); Arrays.sort(temp, new Comparator<String>(){ public int compare(String s1, String s2) { return s1.length() == s2.length() ? s1.compareTo(s2) : s1.length() - s2.length(); } }); for(String s:temp) System.out.print(s+" "); // 1 9 11 12 33 34 45 45 90 221 235 345 355 456 552 } }
3、泛型理解
在编译期间,所有的泛型信息都会被擦除掉,只会在编译期间来是实现。
在生成的字节码中是不会又泛型的类型信息的使用泛型的时候加上的类型参数。在编译期间都会去掉,换成实际类型参数!
如在代码中定义的List<object>和List<String>等类型,在编译后都会编程List。JVM看到的只是List,而由泛型附加的类型信息对JVM来说是不可见的。
示例1----->: 通过反射还是可以往里添加不同类型! ArrayList<Integer> arrayList=new ArrayList<Integer>(); arrayList.add(1); arrayList.getClass().getMethod("add", Object.class).invoke(arrayList, "2"); --------------------------------------------------------- 示例2:---->: 运行类相同 List<String> a1 = new ArrayList<String>(); a1.add("sutdy"); List<Integer> a2 = new ArrayList<Integer>(); a2.add(60); System.out.println(a1 instanceof ArrayList<?>); System.out.println(a1.getClass()); System.out.println(a1.getClass() == a2.getClass()); // true class java.util.ArrayList true ----------------------------------------------------------- 示例3:---->: 引用检查 /* 类型检查就是针对引用的,谁是一个引用,用这个引用调用泛型方法, 就会对这个引用调用的方法进行类型检测,而无关它真正引用的对象。*/ private static void test2() { // -------------------1---------------------- ArrayList<String> a1 = new ArrayList(); a1.add("2"); // only need to add String // a.add(2); error; /* new ArrayList()只是在内存中开辟一个存储空间, 可以存储任何的类型对象。 【 而真正涉及类型检查的是它的引用 】 因为我们是使用它【引用】a1 来调用它的方法, 比如说调用add()方法。所以a1引用能完成泛型类型的检查。 */ //---------------------2----------------------- ArrayList a2 = new ArrayList<String>(); a2.add("2"); a2.add(2); // no error // can be added Object }
例子中,我们定义了两个ArrayList数组,不过一个是ArrayList<String>泛型类型,只能存储字符串。
一个是ArrayList<Integer>泛型类型,只能存储整形。最后,我们通过a1对象和a2对象的getClass方法获取它们的类的信息,最后发现结果为true。
说明泛型类型String和Integer都被擦除掉了,只剩下了原始类型。
什么是原始类型?
原始类型(raw type)就是擦除去了泛型信息,最后在字节码中的类型变量的真正类型。无论何时定义一个泛型类型,相应的原始类型都会被自动地提供。类型变量被擦除(crased),并使用其
限定类型(无限定的变量用Object)替换。java编译器是通过先检查代码中泛型的类型,然后再进行类型擦除,在进行编译的。
4、泛型限定
1、泛型的参数类型可以使用extends语句,例如<T extends superclass>。习惯上称为“有界类型”<T extends Collection>这里的限定使用关键字 extends,后面可以是类也可以是接口。但这里的extends已经不是继承的含义了,应该理解为T类型是实现Collection接口的类型,或者T是继承了XX类的类型。
2、通配符泛型不单可以向下限制,如<? extends Collection>,还可以向上限制,如<? super Double>,表示类型只能接受Double及其上层父类类型,如Number、Object类型的实例。
private static void test1() { ArrayList<Object> a1 = null; a1 = new ArrayList<Object>(); a1.add("Object"); //--------------为什么不可以有继承关系呢?-------------- /* Error ArrayList<Object> a2 = null; a2 = new ArrayList<String>(); a2.add("df"); */ /* 上面面为什么会有错呢?不是有继承关系么? 假如允许的话,a2 得到了 new ArrayList<String>() a2 就可以往里添加 String 也可以往里添加继承了Object的任意对象,如:Integer 这会导致ArrayList内部类型不同,与泛型初衷违背! 对上面的改进 ,可以有继承实现的 形式,如下: */ //上限限制:-------<? extends superClass>--------------- // ArrayList<Integer> a3 = new ArrayList<Integer>(); a3.add(888); ArrayList< ? extends Number> temp1 ; temp1 = a3 ; // temp1.add(8882); 也有编译不过, 重在 get System.out.println(temp1.get(0).getClass()); //下限限制:---------<? super sonClass>------------------ ArrayList<Number> a4 = new ArrayList<Number>(); a4.add(999.00); ArrayList<? super Integer> temp2 ; temp2 = a4; temp2.add(89); // Integer n = temp2.get(0); 编译不过 System.out.println(temp2.get(0).getClass()); //------------------------------------------------------------ }
private static void test2() { ArrayList<String> a1 = new ArrayList<String>(); a1.add("abc"); a1.add("def"); a1.add("hij"); _iterator1(a1); ArrayList<Integer> a2 = new ArrayList<Integer>(); a2.add(11); a2.add(12); a2.add(13); // _iterator1(a2); error _iterator2(a2); } private static void _iterator1(ArrayList<String> a) { Iterator<String> i = a.iterator(); while(i.hasNext()){ System.out.print(i.next()+" "); } System.out.println(); } private static void _iterator2(ArrayList<?> a) { // private static<TT> void _iterator3(ArrayList<TT> a) Iterator<?> i = a.iterator(); while(i.hasNext()){ System.out.print(i.next()+" "); } System.out.println(); }