泛型小结

来源:互联网 发布:网络低安全性 怎么解决 编辑:程序博客网 时间:2024/05/16 07:11

泛型小结

1.先看一个例子,通过反射构造对象:

……

try {

      Constructor c = String.class.getDeclaredConstructor();

      //方法签名:public Constructor<T> getConstructor(Class<?>... parameterTypes)

      String s = (String) c.newInstance(new Object[]{});//需要转型

      //方法签名:public T newInstance(Object ... initargs)

      System.out.println(s);

      

      Constructor<String> c1 = String.class.getConstructor(StringBuffer.class);

      String s1 = c1.newInstance(new StringBuffer("wangliang1"));//对于泛型就不需要转型

      System.out.println(s1);

      

      //对于没有参数的构造方法,这样获得对象

      //Class.getConstrutor(new Class[]{}|null|).newInstance(new Object[]{}|null|),而且不一定要对应     

} catch (Exception e) {

}

Constructor<String> c1 = String.class.getConstructor(StringBuffer.class);

      String s1 = c1.newInstance(new StringBuffer("wangliang1"));

由于使用了泛型,就不需要转型。

 

2.对于使用了泛型的集合,如果没有使用泛型,则有unchecked警告。

 

3.从上面可以看到,对于定义的泛型,也可以不使用。自定义的泛型类,也可以不使用泛型:

 

import java.lang.reflect.Constructor;

public class TestNumber {

      public static void main(String[] args){

             //没有使用泛型,unchecked警告

             //没有用泛型,所以add的签名为add(Object t1,Object t2),null也是对象

             new Test().add(null, null);

             new Test().add(new Object(), new String());

 

             //没有使用泛型,unchecked警告

             //没有用泛型,所以subtract的签名为subtract(Object... args),null也是对象,0个参数也行

             new Test().subtract(new Object[]{"a","b"});//打印2

             new Test().subtract(new String[]{"a","b"});//打印2(说明会拆开,当成3个对象)

             new Test().subtract(new String[]{});     //打印0

             new Test().subtract(new int[]{'a','b'});   //打印1

             new Test().subtract();                                   //打印0

             new Test().subtract(null);                              //打印0(不处理可能空指针异常)

      }

 

}

 

class Test<T>{

      void add(T t1,T t2){

             System.out.println("泛型方法执行");

      }

      void subtract(T...args){

             int len =(args==null)?0:args.length;

             System.out.println("【泛型方法2执行】参数的长度为"+len);

             //System.out.println("【泛型方法2执行】参数的长度为"+args.length);对于new Test().subtract(null);

             //会造成空指针异常;但是new Test().subtract();不会

      }

}

 

4.泛型编译之后去掉了泛型的参数化信息(即不保留泛型参数的信息),也就是说

ArrayList<String>.class==ArrayList<Integer>.class==ArrayList.class

都是class java.util.ArrayList

 

5.进一步,编译之后方法中的泛型也去掉了,例如ArrayListadd(E e)方法,以下

 

System.out.println(ArrayList.class.getMethod("add", Object.class));

//不能使用ArrayList<String>.class.getMethod("add", Object.class)

System.out.println(new ArrayList<String>().getClass().getMethod("add", Object.class));

都是打印:

public boolean java.util.ArrayList.add(java.lang.Object)

public boolean java.util.ArrayList.add(java.lang.Object)

所以泛型不能作为方法签名的一部分:

<S> void medthod(S s){}

<E> void medthod(E e){}

上面两个方法相同,编译出错

void medthod(ArrayList<S> s){}

void medthod(ArrayList<T> t){}

上面两个方法相同,编译出错

 

 

6.对于自定义的泛型类

class Test<T>{

      void add(T t1,T t2){

             System.out.println("【泛型方法1执行】");

      }

      void subtract(T...args){

             int len =(args==null)?0:args.length;

             System.out.println("【泛型方法2执行】参数的长度为"+len);

             //System.out.println("【泛型方法2执行】参数的长度为"+args.length);对于new Test().subtract(null);

             //会造成空指针异常;但是new Test().subtract();不会

      }

}

 

客户端调用

//public final native Class<?> getClass();

Class<? extends Test> tc1 = new Test<String>().getClass();

Class<Test> tc2 = (Class<Test>) new Test<String>().getClass();

Class tc3 = new Test<String>().getClass();

             

Class tc4 = new Test().getClass();

Class<? extends Test> tc5 = new Test().getClass();

Class<Test> tc6 = (Class<Test>) new Test().getClass();

其中tc1-tc6打印出来结果都是一样的(new Test().getClass()得到的是Class<? extends Test>,所以可能需要转型)

 

//public Method getDeclaredMethod(String name, Class<?>... parameterTypes)

System.out.println(new Test().getClass().getDeclaredMethod("add", new

Class[]{Object.class,Object.class}));

 

System.out.println(new Test<String>().getClass().getDeclaredMethod("add", new

Class[]{Object.class,Object.class}));

打印的结果是一样的add的签名是void add(Object ,Object)  

 

7.泛型编译之后去掉了泛型的参数化信息,所以可以通过反射做以下的事情

ArrayList<Integer> list = ArrayList<Integer>();

list.add("a");//编译出错

list.getClass().getMethod("add",Object.class).invoke(list,"a");//但是这样可行

 

8.ArrayList<E>称为泛型类型

ArrayList<E>中的E称为类型参数

ArrayList<Integer>称为参数化的类型,Integer称为实际类型参数

ArrayList称为原始类型

 

9.JDK1.5之前没有泛型,为了与以前兼容,下面都是允许的:

ArrayList list1 = new ArrayList();

ArrayList<String> list2 = new ArrayList<String>();

ArrayList list3 = new ArrayList<String>();

ArrayList<String> list4 = new ArrayList();

 

10.参数化的类型无继承关系,下面都是错误的

ArrayList<Object> list1 = new ArrayList<String>();

ArrayList<String> list2 = new ArrayList<Object>();

但是

ArrayList list = new ArrayList<String>();

ArrayList<Object> list1 = list ;

却不会报错

 

11.关于泛型数组,下面

ArrayList[] array1 = new ArrayList[2];

ArrayList<Integer>[] array2 = new ArrayList[2];

ArrayList[] array3 = new ArrayList<Integer>[2];//错误

ArrayList[] array4 = new ArrayList<Integer>[2];//错误

 

12.ArrayList<E>中的E是一种类型(只是现在还没确定,实例化对象是确定),传入的参数只能匹配一种对象;如果要使得类型参数可以接收任意对象,使用<?>

如果在类中定义一个方法:

 

//E是类级别的类型参数

void medthod(ArrayList<E> a),那么E匹某一种对象或者无泛型的ArrayList的类型

void medthod(ArrayList<Object> a)也只能匹配ArrayList<Object>或者无泛型的ArrayList的类型(参数化的类型无继承关系,参数为ArrayList<String>出错)

 

我们可以

void medthod(ArrayList<?> a)其中?匹配任意对象或者无泛型的ArrayList的类型

 

需要注意的是:

void medthod(ArrayList<?> a){

      ……

      a.add("abc");//出错,因为?还是未知,有无add(String)也是未知

      a.size();

      ……

}

而对于下面

void medthod(ArrayList<T> a){

      ……

      a.add("abc");//因为T还是未知,有无add(String)也是未知

a.add((T) "a");//强制转换,但类型不一定匹配

      a.size();

      ……

}

 

13.例子:打印集合

public void printCollection(Collection<?> c){

      for(Object obj:c){

             System.out.println(obj);

      }

}

或者:

//注意这里E是方法级别的

public <E> void printCollection(Collection<E> c){

      for(Object obj:c){

             System.out.println(obj);

      }

      //c.add(T类型的变量)也是可行;复制此集合也是可以的,上面的就不行

}

上面两个方法的参数可以是泛型参数为任意对象的集合或者无泛型的集合

注意:Collectionpublic interface Collection<E> extends Iterable<E>

 

14.对于forName()

Class cl = Class.forName("java.lang.String");//要处理异常

Class<String> c34 = (Class<String>) Class.forName("java.lang.String");//要处理异常

Class<?> c35 = Class.forName("java.lang.String");//要处理异常

forName()返回Class<?>类型,对比一下

 

Class<String> cl1 = String.class;

//.class返回类的类类型

 

//对象getClass()返回Class<? extends 对象的类名>

Class<String> cl2 = (Class<String>) "String".getClass();

Class<? extends String> cl3 = "String".getClass();

Class<? extends Number> n= Number.class.asSubclass(Number.class);

//方法签名:public <U> Class<? extends U> asSubclass(Class<U> clazz)

 

注意含有通配符的泛型不能放到赋值运算符右边

 

15.通配符?,确定泛型的边界

ArrayList<? extends Number> a = new ArrayList<Integer>();

ArrayList<? super Integer> a = new ArrayList<Number>();

//当然没有泛型参数也是可行的:

ArrayList<? extends Number> a = new ArrayList();

ArrayList<? super Integer> a = new ArrayList();

extendssuper可以指定泛型的边界,可以有多个边界,如

<T extends Serizaliable&Clonable> method(){}

 

16.例子:使用泛型打印Map集合

 

HashMap<Integer, String> maps = new HashMap<Integer,String>();

maps.put(1, "wangliang");

maps.put(2,"hpa");

maps.put(3, "syj");

Set<Map.Entry<Integer, String>> entrySet = maps.entrySet();

for(Map.Entry<Integer, String> entry:entrySet){

      System.out.println(entry.getKey()+":"+entry.getValue());

}

 

JSP中标签:

<c:forEach items="${map}" var="entry">

      ${entry.key}:${entry.value}

</c:forEach>

 

17.泛型的类型推断。

1)对于下面的方法

static <T> T add(T t1,T t2){

      return null;

}

客户端调用

Integer i =add(3,8);

Double num2 =add(4d,9d);//整数要加d才是double

Double num3 =add(4.9,9.0);//小数数默认是double

Float f = add(8.9f,3f);//浮点数要加f

 

//不同类型的数字就是Number

Number num =add(4,9.9);//<? extends Number>

Number num22 =add(4f,9.9);//<? extends Number>

 

Object o =add(8,"a");

 

2)对于下面两个方法

public static <T> void copy1(Collection<T> dest,T[] src){

………………             

}

      

public static <T> void copy2(T[] dest,T[] src){

………………             

}

这样使用

copy1(new Vector<String>(),new String[10]);

copy2(new Stringr<String>(),new String[10]);

是没问题的,但是,

copy2(new Date[10],new String[10]);

//new Date[10]new String[10]当做Object数组           

copy1(new Vector<Date>(),new String[10]);

//编译错误,Collection<T>中的TT[]T不匹配

 

3)对于下面的方法:

static <T> void swap(T[] t,int i,int j){

      if((t != null) && (i < t.length ) && (j < t.length)){

             T tmp = t[i];

             t[i]=t[j];

             t[j]=tmp;

      }

      

}

swap(new Object[]{1,2},1,3);

swap(new String[]{"a","b"},1,3);

swap(new String[]{"a","b"},1,3);

swap(null,1,3);

swap(new int[]{},1,3);//错误,泛型只能匹配对象

4)前面的有些方法根据参数类型得到相应的类型,怎么实现的?一个例子:

static <T> T convert(Object o){

      return (T)o;

}

String wangliang = convert("wang");

Integer inte = convert(8);

这是根据返回值推断泛型的具体类型

 

 

18.各种方法都可以使用泛型

 

19.泛型不能在catch中抓住

 

{

}catch(T){//错误

      throw (T)e;   

}

但是可以抛出

<T extends Exception> void medthod() throws T

 

20其他

类级别的泛型保证同一个字母表示的是同一种类(方法级别的不行)

静态方法不能使用类级别的泛型,可以使用方法级别的泛型

原创粉丝点击