Java泛型

泛型定义

泛型:类型参数化。提到参数化一般想到的是定义方法时的形参,调用时传入实参。泛型指的是将原来具体的类型参数化,类似方法的形参,在定义时声明为类型参数,当调用/使用时传入具体的类型实参。

泛型好处

1类型安全:泛型的主要目标是提高java程序的类型安全。通过泛型定义的变量的类型限制,编译器可以在一个高得多的程度上验证类型假设。

2)消除强制类型转换:消除强制类型转换,减少出错机会。

3)潜在的性能效益:将运行期的类型检查转移到编译期

泛型实现原理

Java的泛型基本完全在编译器中实现,由编译器执行类型检查类型推断,然后生成普通的非泛型的字节码,这种实现技术叫“擦除”。(编译器使用泛型类型信息保证类型安全,然后在生成字节码之将其清除)

1、类型擦除

1)类型擦除指的是通过类型参数合并,将泛型类型实例关联到同一份字节码上。编译器只为泛型类型生成一份字节码,并将其实例关联到这份字节码上,因此泛型类型中的静态变量是所有实例共享的。(一个static方法无法访问泛型类的类型参数,因为类还没有实例化,所以如果static方法需要使用泛型能力,必须使其成为泛型方法)

2)泛型类型是被所有调用共享的

List<String>l1 = new ArrayList<String>();  

List<Integer>l2 = new ArrayList<Integer>();  

System.out.println(l1.getClass() == l2.getClass()); //True

3)类型擦除的关键在于:从泛型类型中清除类型参数的相关信息,并且在必要的时候添加类型检查和类型转换的方法。在使用泛型时,任何具体的类型都被擦除,唯一知道的是你在使用一个对象。例如:List<String>List<Integer>在运行期间是相同的类型List,因为他们被擦除为原生类型List

例子:因为编译期的类型擦除,所以不能通过同一个泛型类的实例来区分方法。会导致编译时错误。

public void test(List<String> ls){  
             System.out.println("Sting");  
            }  
            public void test(List<Integer> li){  
                System.out.println("Integer");  
            }

4)在编译期进行类型擦除(移除了类型信息)了,那如何准确的进行类型转换?

运行时的边界:即对象进入和离开方法的地方,这正是编译器在编译期执行类型检查并插入转型代码的地点。泛型中的所有动作都发生在边界处:对传递进来的值进行额外的编译期检查,并插入对传递出去的值的转型。

2、泛型不是协变的

Java数组是协变的(covariant),协变的意思是:因为Integer扩展了Number,那么不仅IntegerNumber,而且Integer[]也是Number[],在要求Number[]的地方完全可以传递或赋值Integer[],即NumberInteger的超类,Number[]也是Integer[]的超类。

泛型不是协变的,NumberInteger的超类,但List<Integer>List<Number>没有关系,这点是为了保证类型安全。

List<Integer> intList = new ArrayList<Integer>();
List<Number> numberList = intList; // invalid
numberList.add(new Float(3.1415));

因为 intList numberList 都是有别名的,如果允许的话,上面的代码就会让您将不是Integers 的东西放进intList 中。

3、类型通配符

类型通配符是使用?来代替具体类型参数。List<?>List<String>List<Integer>List<Number>..等所有List<具体类型实参>的父类。定义类型通配符来实现多态

//使用通配符?,表示可以接收任何元素类型的集合作为参数  

void printCollection(Collection<?> c) {
for (Object e:c){
System.out.println(e);
}
}

使用了通配符?指定可以使用任何类型的集合作为参数。读取的元素使用了Object类型来表示,这是安全的,因为所有的类都是Object的子类。这里就又出现了另外一个问题,如下代码所示,如果试图往使用通配符?的集合中加入对象,就会在编译时出现错误。需要注意的是,这里不管加入什么类型的对象都会出错。这是因为通配符?表示该集合存储的元素类型未知,可以是任何类型。往集合中加入元素需要是一个未知元素类型的子类型,正因为该集合存储的元素类型未知,所以我们没法向该集合中添加任何元素。唯一的例外是null,因为null是所有类型的子类型,所以尽管元素类型不知道,但是null一定是它的子类型。

Collection<?> c=new ArrayList<String>();  
c.add(newObject()); //compile time error,不管加入什么对象都出错,除了null外。  
c.add(null); //OK  

1)如果你想从一个数据类型里获取数据,使用<? extends XX> 类型通配符上限--可以定义XX的任何子类

2)如果你想把对象写入一个数据结构里,使用<? super XX>类型通配符下限---可以定义XX的任何超类

3)如果你既想存,又想取,那就别用通配符。

程序案例:

public  <T> void go(T t) {  
    System.out.println("generic function");  
}  
public void go(String str) {  
    System.out.println("normal function");  
}  
public static void main(String[] args) {  
        FuncGenric fg = new FuncGenric();  
        fg.go("haha");//打印normal function  
        fg.<String>go("haha");//打印generic function  
        fg.go(new Object());//打印generic function  
        fg.<Object>go(new Object());//打印generic function  
}

(1)当不指定类型参数时,会调用普通方法

(2)如果指定了类型参数,则调用泛型方法,因为泛型方法在编译阶段进行擦除,则泛型方法为public Object go(Object t)。普通方法是String类型,而泛化类型方法参数为Object

 

instanceOf

不能对确切的泛型类型使用instanceOf操作。如下面的操作是非法的,编译时会出错。

Collection cs = new ArrayList<String>();  
if (cs instanceof Collection<String>){…}// compile error.如果改成instanceof Collection<?>则不会出错。  

泛型数组问题

不能创建一个确切泛型类型的数组。如下面代码会出错。

List<String>[] lsa = new ArrayList<String>[10]; //compile error.

只能创建带通配符的泛型数组,如下面例子所示,这回可以通过编译,但是在倒数第二行代码中必须显式的转型才行,即便如此,最后还是会抛出类型转换异常,因为存储在lsa中的是List<Integer>类型的对象,而不是List<String>类型。最后一行代码是正确的,类型匹配,不会抛出异常。

List<?>[] lsa = new List<?>[10]; // ok, array of unbounded wildcard type  
Object o = lsa;  
Object[] oa = (Object[]) o;  
List<Integer>li = new ArrayList<Integer>();  
li.add(new Integer(3));  
oa[1] = li; //correct  
String s = (String) lsa[1].get(0);// run time error, but cast is explicit  
Integer it = (Integer)lsa[1].get(0); // OK   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值