java泛型

定义

泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。

为什么需要泛型

Java语言引入泛型的好处是安全简单。可以将运行时错误提前到编译时错误。
在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。

语法

(1)命名风格

用简练的名字作为形式类型参数的名字(单个大写字母,例如< T >)

(2)泛型类

类中定义完类型参数后,可以在定义位置之后的类的几乎任意地方(静态块,静态属性,静态方法除外)使用类型参数,就像使用普通的类型一样。
注:父类定义的类型参数不能被子类继承。
GenericClass.java

package genericity;
public class GenericClass<E> {  
    private E e;//变量  
    public GenericClass(){}     
    public GenericClass(E e){  
        this.e=e;  
    }     
    public E getE(){//返回值类型  
        return e;  
    }     
    public void println(E e){//函数参数  
        System.out.println(e);  
    }  
    public static void main(String[] args) {  
        GenericClass<Integer> gc=new GenericClass<Integer>();  
        gc.println(123);  
        GenericClass<String> gc1=new GenericClass<String>();  
        gc1.println("string");                  
    }  
}

(3)泛型接口

GenericInterface.java

package genericity;
public interface GenericInterface<E> {          
    public E getE();  
    public void save(E e);    
} 

泛型接口实现类有两种实现方法,都是为接口创建相应的实现类。区别为一种泛型接口的子类不含类型形参;另一种泛型接口子类包含类型形参,使用时传递实参。

GenericInterImpl1.java

package genericity;
public class GenericInterImpl1 implements GenericInterface<Integer>{
    @Override
    public Integer getE() {
        // TODO Auto-generated method stub
        return null;
    }
    @Override
    public void save(Integer e) {
        // TODO Auto-generated method stub
    }  

}   

GenericInterImpl2.java

public class GenericInterImpl2<E> implements GenericInterface<E>{

    @Override
    public E getE() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void save(E e) {
        // TODO Auto-generated method stub
    }  

}

(4)泛型方法

package genericity;
class Demo{  
    public <T> T func(T t){            // 可以接收任意类型的数据  
        return t ;                  // 直接把参数返回  
    }  
}

public class GenericFunction{  
    public static void main(String args[]){  
        Demo d = new Demo() ;   // 实例化Demo对象  
        String str = d.func("string") ; //   传递字符串  
        int i = d.func(123) ;     // 传递数字,自动装箱  
        System.out.println(str) ;   // 输出内容  
        System.out.println(i) ;     // 输出内容  
    }  
}

类型擦除

泛型只在编译阶段有效

ArrayList<String> a = new ArrayList<String>();  
ArrayList b = new ArrayList();  
Class c1 = a.getClass();  
Class c2 = b.getClass();  
System.out.println(c1 == c2); //true  

上面程序的输出结果为true。所有反射的操作都是在运行时的,既然为true,就证明了编译之后,程序会采取去泛型化的措施,也就是说Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,成功编译过后的class文件中是不包含任何泛型信息的。泛型信息不会进入到运行时阶段。

通配符

(1)定义

如下代码,虽然Integer是Number的子类,但Box< Integer >不是Box< Number >的子类型,所以编译会报错。同理List< Integer > 不是 List< Number >的子类。

package genericity;
public class GenericDemo {

    public static void main(String[] args) {
          Box<Number> name = new Box<Number>(99);
          Box<Integer> age = new Box<Integer>(712);
          getData(name);
          //编译报错
          getData(age);   // 1
    }

    public static void getData(Box<Number> data){
        System.out.println("data :" + data.getData());
    }
 }

解决当具体类型不确定的时候,这个通配符就是 ? ,用 ? 通配符来表未知类型。
类型通配符一般是使用 ? 代替具体的类型实参。注意了,此处是类型实参,而不是类型形参!且Box

package genericity;
public class GenericDemo {
    public static void main(String[] args) {
          Box<Number> name = new Box<Number>(99);
          Box<Integer> age = new Box<Integer>(712);
          getData(name);
          getData(age);   // 1
    }
    public static void getData(Box<?> data){
        System.out.println("data :" + data.getData());
    }
}

by the way,如仅仅是想实现多态,请优先使用通配符解决。
public < T > void testGenericMethodDefine2(List< T > s){

}
应改为
public void testGenericMethodDefine2(List<?> s){

}

(2)上限

List

package genericity.demo;
abstract class Animal {
    public abstract void run();
}

Dog.java

class Dog extends Animal {
    public void run() {
        System.out.println("Dog run");
    }
}

App.java

package genericity.demo;
import java.util.ArrayList;
import java.util.List;

public class App {
    public static void getSize(List<Animal> list) {
        System.out.println(list.size());
    }
    public static void main(String[] args) {
        List<Dog> list = new ArrayList<>();
        getSize(list); // 这里编译报错 
    }
} 

这里编程出错的原因是List< Animal >并不是List< Dog >的父类。解决方案一可以把getSize方法中形参List< Animal >改为List< ? >,不过这样的话在每次get对象的时候都要强制类型转换,比较麻烦。使用通配符上限很好的解决了这个问题,可以把List< Animal >改为List< ? extends Animal >,编译就不会错了,也不用类型转换。

(3)下限

List

class Cat extends Animal {
    public void run() {
        System.out.println("Cat run");
    }
}

App2.java

package genericity.demo;
import java.util.ArrayList;
import java.util.List;

public class App2 {
    public static void getSize(List<? super Dog> list) {
        System.out.println(list.size());
    }
    public static void main(String[] args) {
        List<Cat> list = new ArrayList<>();
        getSize(list); // 这里编译报错
    }
} 

泛型的好处

(1)类型安全

通过知道使用泛型定义的变量的类型限制,编译器可以更有效地提高Java程序的类型安全。

(2)消除强制类型转换

消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。所有的强制转换都是自动和隐式的。

(3)提高性能

参考

http://blog.youkuaiyun.com/jinuxwu/article/details/6771121/
http://www.cnblogs.com/lzq198754/p/5780426.html
http://blog.youkuaiyun.com/hgd613/article/details/12656073
http://www.cnblogs.com/lwbqqyumidi/p/3837629.html
http://www.jb51.net/article/64072.htm

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值