Chinaunix首页 | 论坛 | 博客
  • 博客访问: 69908
  • 博文数量: 43
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 420
  • 用 户 组: 普通用户
  • 注册时间: 2014-06-27 15:04
个人简介

记录,分享

文章分类

全部博文(43)

文章存档

2017年(24)

2015年(1)

2014年(18)

我的朋友

分类: Java

2017-03-16 11:44:33

public class GenericSample {
   private T first;
   private U second;
   public GenericSample(T first, U second) {
       this.first = first;
       this.second = second;
   }
   /*
    * 泛型方法可以定义在普通类中,也可以定义在泛型类中
    */
   public K test2(T t, L l){
       return null;
   }
   /*
    * 为泛型类型加约束,多个约束可用&分隔
    */
   public static int test1(S s){
       return s.capacity();
   }
}

1. 泛型类与泛型方法的定义,以及类型变量限定,见GenericSample。

2. 泛型代码与虚拟机
(1)虚拟机如何看待泛型类型:
   虚拟机里不存在泛型类型对象,所有对象都属于普通类。无论何时定义了一个泛型类型,都自动提供了一个原始类型(raw type),jvm使用的是原始类型。以GenericSample为例, 其原始类型是:
public class GenericSample= {
   private Object first;
   private Object second;
   public GenericSample(Object first, Object second) {
       this.first = first;
       this.second = second;
   }
   public Object test2(Object t, Object l){
       return null;
   }
   public static  int test1(Buffer s){
       return s.capacity();
   }
}

(2)虚拟机如何翻译泛型表达式:
   泛型类型转换成原始类型时,类型变量会被替换为限定类型的第一个(称为“擦除/erase”),如test1方法的参数类型为Buffer, 如果没有限定类型,则转换成Object类型。
   当返回类型需要被擦除,调用泛型方法时虚拟机会自动插入强制转换。如
List list = new ArrayList();
String s = list.get(0);
第二行代码编译器会将其转换为两条指令:(1)调用原始类型的get方法(2)将返回值的类型由Object强制转换成String。

(3)虚拟机如何翻译泛型方法:
当泛型与多态发生冲突时,虚拟机通过“桥方法”能够正确处理。如下 , 子类继承自泛型类型,按照常理原始类型的Object getT 与 String getT发生冲突,但实际上虚拟机能够正确处理。

   public static void main(String[] args) {
       GenericBase sub = new GenericSub("sub");
       sub.display("haha");
       System.out.println(sub.getT());
   }
   
   class GenericBase {
       public void display(T s){
           System.out.println("Display by base : " + s);
       }
       public T getT(){
           return  (T)("t from base");
       }
       public void test(T t){ }
   }
   
  class GenericSub extends GenericBase{
       public void display(String s){
           System.out.println("Display by sub : " + s);
       }
       public String getT(){
           return "t from sub";
       }
       /*
        * test声明无法通过编译,因为与GenericBase原始类型的擦除后的test方法产生冲突
        */
//        public void test(Object o){          
//        }
   }
输出:
Display by sub : haha
t from sub
true
true

3. 约束与局限性

(1)运行时查询只适用于原始类型.
比如
List l = new ArrayList;
//可以通过编译
if(l instanceof List ) {
  ...
}
//可以通过编译
if(l instanceof List) {
 ...
}
//编译出错
if (l instanceof List){
  ...
}
(2)不能抛出也不能捕获泛型类型实例

(3)不能声明参数化类型的数组,例如
List[] listArray = new List[10];    //编译出错
因为数组可以检查赋值操作的类型,List[]其原始类型为List[],如果声明可以成功,那么listArray  = new List()也能成功,违背了数组检查类型的原则。

(4)不能实例化类型变量.
例如在GenericSample的方法中使用
T t = new T();    //编译出错
T t = T.class.newInstance();    //编译出错
要想实例化类型变量,需要用如下形式:
public T test( Class cls ) {
return cls.newInstance();
}

(5)不能在静态域中使用类型变量

4. 泛型与继承:
无论T与S有何种继承关系,List与List都不存在继承关系。如果需要通过类型变量让泛型类型产生继承关系,可以使用通配符类型。

5. 通配符类型
   对于一个方法,如public void foo(List l) , 是无法将List类型的参数传给它的,解决的方法是用通配符类型: public voidfoo(List l) 。

   List 是List的子类型。

   但是,对于一个泛型类型来说,如果其类型变量为T,其某个方法foo包含了泛型变量作为参数类型,那么及时将这个泛型类型对象a赋给其子类变量b(类型变量为? extends T),b也无法用T类型的参数调用foo,但它可以调用返回值为 ? extends T 类型的方法。如下
List x0 = new ArrayList();
x0.add("000");
List y0 = x0;
y0.add("0000");    //ERROR
CharSequence cs = y0.get(0);    //OK

   相反地,如果用超类限定符 ? super T ,那么可以调用T类型参数的方法,但是无法调用返回值为 ? super T的方法,如下
List x1 = new ArrayList();
x1.add("111");
List y1 = x1;
y1.add("1111");    //OK
CharSequence  cs = y1.get(0);     //ERROR
String s = y1.get(0);      //ERROR

   此外,还有无限定通配符,
List x2 = new ArrayList();
x2.add(new Object());
List y2 = x2;
y2.add(new Object());  //ERROR
Object o = y2.get(0);

?适用于不需要类型变量的场景,例如
static boolean isEmpty(List  list) {
   return list == null || list.isEmpty();
}
它和下面的方法是等效的,但是可读性更强一些
static boolean isEmpty(List list){
   ...
}

6. 泛型与反射
package function.lang;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.util.Collection;
public class GenericReflection {
   public static void main(String[] args) throws NoSuchMethodException, SecurityException {
       MyCollection mycol = new MyCollection();
       TypeVariable[] tvs = mycol.getClass().getTypeParameters();
       for(TypeVariable tv : tvs){
           System.out.print(tv + " : ");
           for(Type t : tv.getBounds()){
               System.out.print(t + ",");
           }
           System.out.println();
       }
       System.out.println("---------");
       Method m = mycol.getClass().getMethod("add", Object.class);
       tvs = m.getTypeParameters();
       for(TypeVariable tv : tvs){
           System.out.print(tv + " : ");
           for(Type t : tv.getBounds()){
               System.out.print(t + ",");
           }
           System.out.println();
       }
   }
}

class MyCollection {
   public void add(T t){  }
   public void addCharSequence(Collection seq){ }
}

输出:
T : class java.lang.Object,
S : class java.nio.Buffer,interface java.lang.Comparable,
---------
T : class java.lang.Object,
阅读(309) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~