1回顾泛型类
泛型类:具有一个或多个泛型变量的类被称之为泛型类。
class ClassGenericity{ //在类里面可以直接使用T的类型 T aa; public void test11(T bb) { //................ } //静态方法 在类上面定义的泛型,不能再静态方法里面使用 public static void test12(A cc) { //.................. } } public class TestClassGenericity{ public static void main(String[] args) { ClassGenericity genericity=new ClassGenericity (); genericity.test11("1234"); ClassGenericity.test12(6789); } }
2 泛型方法
泛型方法的特点:
方法的参数中可以使用泛型变量;
方法的返回值中可以使用泛型变量。
public class MethodGenericity { public static void main(String[] args) { String arr[]={"aaa","bbb","ccc","ddd"}; System.out.println(Arrays.toString(arr)); exchange(arr,0,3); //交换0,3位置元素 System.out.println(Arrays.toString(arr)); } private staticvoid exchange(T[] arr, int i, int j) { T temp=arr[i]; arr[i]=arr[j]; arr[j]=temp; } }
public class Test1 { publicT get(T[] ts) { return ts[ts.length / 2]; } @Test public void test() { String[] names ={"zhangSan", "liSi","wangWu"}; System.out.println(get(names));//输出lisi } }
调用泛型方法时无需指定泛型变量,编译器会通过实际参数的类型来识别泛型变量的类型,上例中传递的参数为String[]类型,那么相当于给泛型类型T赋值为String。
3 继承(实现)泛型类(接口)
继承泛型类需要为父类的泛型变量赋值!就好比创建泛型类的对象时需要给泛型变量赋值一样。
创建泛型类对象:List
继承泛型类1(子类也是泛型类):public class MyList1
继承泛型类2(子类不是泛型类):public class MyList2 extends ArrayList
4 通配符
为了说明通配符的作用,我们先看个例子:
List
List
上面的调用都是编译不通过的!这说明想写一个即可以打印list1,又可以打印list2的方法是不可能的!
public static void fun(List
如果把fun()方法的泛型参数去除,那么就OK了。即不使用泛型!
public static void fun(List list) {…}//会有一个警告 Listlist1 = new ArrayList (); List list2 = new ArrayList (); fun(list1); fun(list2);
上面代码是没有错了,但会有一个警告。警告的原因是你没有使用泛型!Java希望大家都去使用泛型。你可能会说,这里根本就不能使用泛型!!!!!
4.1 通配符概述
通配符就是专门处理这一问题的。
public static void fun(List> list) {…}
上面代码中的“?”就是一个通配符,它只能在“<>”中使用。这时你可以向fun()方法传递List
4.2 通配符的缺点
带有通配符的参数不能使用与泛型相关的方法,例如:list.add(“hello”)编译不通过。
上面的问题是处理了,但通配符也有它的缺点。在上面例子中,List> list参数中的通配符可以被赋任何值,但同时你也不知道通配符被赋了什么值。当你不知道“?”是什么时,会使你不能使用任何与泛型相关的方法。也就是说fun()方法的参数list不能再使用它的与泛型相关的方法了。例如:list.add(“hello”)是错误的,因为List类的add()方法的参数是T类型,而现在你不知道T是什么类型,你怎么去添加String的东西给list呢?如果使用者在调用fun()方法时传递的不是List
4.3 通配符的限制
通配符只能出现在引用的定义中,而不能出现在创建对象中。例如:new ArrayList>(),这是不可以的。ArrayList> list = null,这是可以的。
4.4 带有下边界的通配符
List extends Number> list;
其中 extends Number>表示通配符的下边界,即“?”只能被赋值为Number或其子类型。
public static void fun(List extends Number> list) {…} fun(new ArrayList());//ok fun(new ArrayList ());//ok fun(new ArrayList ());//不ok
当fun()方法的参数为List extends Number>后,说明你只能赋值给“?”Number或Number的子类型。虽然这多了一个限制,但也有好处,因为你可以用list的get()方法。就算你不知道“?”是什么类型,但你知道它一定是Number或Number的子类型。所以:Number num = list.get(0)是可以的。但是,还是不能调用list.add()方法!
4.5 带有下边界的通配符
List super Integer> list;
其中 super Integer>表示通配符的下边界,即“?”只能被赋值为Integer或其父类型。
public static void fun(List super Integer> list) {…} fun(new ArrayList());//ok fun(new ArrayList ());//ok fun(new ArrayList
这时再去调用list.get()方法还是只能使用Object类型来接收:Object o = list.get(0)。因为你不知道“?”到底是Integer的哪个父类。但是你可以调用list.add()方法了,例如:list.add(new Integer(100))是正确的。因为无论“?”是Integer、Number、Object,list.add(new Integer(100))都是正确的。
4.6 通配符小结
1. 方法参数带有通配符会更加通用;
2. 带有通配符类型的对象,被限制了与泛型相关方法的使用;
3. 下边界通配符:可以使用返回值为泛型变量的方法;
4. 上边界通配符:可以使用参数为泛型变量的方法。
4.7 通配符应用实例
boolean addAll(Collection extends E> c)//JDK集合的addAll方法
ListnumList = new ArrayList (); List intList = new ArrayList (); numList.addAll(intList);//正确!!!!!!addAll(Collection extends Number> c), 传递的是List
如果用改成boolean addAll(Collection
ListnumList = new ArrayList (); List intList = new ArrayList (); numList.addAll(intList);//错误!!!!!addAll(Collection c), 传递的是List
5 通配符应用实例
import java.util.ArrayList; import java.util.List; public class Demo2 { public void fun1() { Object[] objArray = new String[10];//正确!!! objArray[0] = new Integer(100);//错误!!!编译器不会报错,但是运行时会抛ArrayStoreException //List//错误!!!编译器报错,泛型引用和创建两端,给出的泛型变量必须相同! } public void fun2() { ListintegerList = new ArrayList (); print(integerList); List stringList = new ArrayList (); print(stringList); } /* * 其中的?就是通配符,?它表示一个不确定的类型,它的值会在调用时确定下来 * 通配符只能出现在左边!即不能在new时使用通配符!!! * List> list = new ArrayList (); * 通配符好处:可以使泛型类型更加通用!尤其是在方法调用时形参使用通配符! */ public void print(List> list) { //list.add("hello");//错误!!!编译器报错,当使用通配符时,对泛型类中的参数为泛型的方法起到了副作用,不能再使用! Object s = list.get(0);//正确!!!但是只是得益于object类是所有类的父类,换成其他任何类编译器都会报错!说明当使用通配符时,泛型类中返回值为泛型的方法,也作废了! } public void fun3() { ListintList = new ArrayList (); print1(intList); List longList = new ArrayList (); print1(longList); } /* * 给通配符添加了限定: * 只能传递Number或其子类型 * 子类通配符对通用性产生了影响,但使用形参更加灵活 */ public void print1(List extends Number> list) { //list.add(new Integer(100));//错误!!!编译器报错,说明参数为泛型的方法还是不能使用(因为?也可能为Long型) Number number = list.get(0);//正确!!!返回值为泛型的方法可用了! } public void fun4() { List intList = new ArrayList (); print2(intList); List numberList = new ArrayList (); print2(numberList); List
6 泛型父类获取子类传递的类型参数
import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import org.junit.Test; public class Demo1 { @Test public void fun1() { new B();//输出:class B java.lang.String } @Test public void fun2() { new C();//输出:class C java.lang.Integer } } class A{ public A() { //在这里获取子类传递的泛型信息,要得到一个Class! Class clazz = this.getClass(); //得到子类的类型 System.out.print(clazz+" "); Type type = clazz.getGenericSuperclass(); //获取传递给父类参数化类型 ParameterizedType pType = (ParameterizedType) type; //它就是A Type[] types = pType.getActualTypeArguments(); //它就是一个Class数组 Class c = (Class)types[0]; //它就是String或者Integer //连成一句:Class c = (Class)((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]; System.out.println(c.getName()); } } class B extends A {} class C extends A {}