java 泛型 窜讲

1、为何使用泛型

     复用性:泛型的本质就是参数化类型,于是使用编写的泛型代码能够被许多不一样类型的对象所复用。java

     安全性:在对类型Object引用的参数操做时,每每须要进行显式的强制类型转换。这种强制类型转换须要在运行时才能被发现是否转换异常,经过引入泛型能将在运行时才能检查类型转换,提早到编译时期就能检查。数组

2、自定义泛型

java中自定义泛型分为三种:泛型类、泛型接口、泛型方法。安全

下面使用一个案例演示泛型类、泛型方法,泛型接口相似,因此再也不演示。app

// 自定义泛型类
public class Generic<T>
{
    private T second;
    public void setSecond(T newValue)
    {
        second = newValue;
    }
    // 自定义泛型方法
    public static <W> void printValue(W obj)
    {
        System.out.println(obj.toString());
    }
   
    @Override
    public String toString()
    {
        return second.toString();
    }
   
    public static void main(String[] args)
    {
        //
        Generic<String> g = new Generic<String>();
        g.setSecond("zhang");
        System.out.println(g);
       
        // 使用泛型方法
        Generic.printValue(45);
        Generic.printValue("hello");
    }
}

泛型方法能够在广泛类中定义,也能够在泛型类中定义。

3、java泛型的特性

一、擦除

       java中的泛型是伪泛型,由于在编译期间,全部的泛型信息都会被擦除,而只保留原始类型。好比,代码List<Double>和List<String>在通过编译后,都会变成List类型。
       为何会出现这种状况呢?这跟java的虚拟机有莫大的关系。java虚拟机没有泛型类型对象-----全部对象都属于普通类。因为在java1.5以前没有泛型,那以前没有泛型的代码怎么与有泛型的代码共存了,为了兼容性,java虚拟机采用统一的普通类。
下面使用代码,体现类型擦除。
public static void main(String[] args)
{
    ArrayList<String> arrayList1=new ArrayList<String>(); 
    arrayList1.add("abc"); 
    ArrayList<Double> arrayList2=new ArrayList<Double>(); 
    arrayList2.add(666.666); 
    System.out.println(arrayList1.getClass()==arrayList2.getClass()); 
}
输出结果:

trueide

二、补偿

public static void main(String[] args)
{
    ArrayList<String> arrayList1=new ArrayList<String>(); 
    arrayList1.add("abc"); 
    String str = arrayList1.get(0);    // 编译正常
    int str2 = arrayList1.get(0);      // 编译报错  
}
arrayList1.get(0);
编译事后,泛型会被擦除,arrayList1.get(0)返回Object类型。可是因为java的补偿机制,所以编译器会自动的插入String的强制类型转换。因为int类型的str2不能接收通过强制转换的String类型,于是编译报错。
 

4、java泛型的约束

一、不能用基本类型实例化类型参数
好比:错误-->Arraylist<double>; 正确-->Arraylist<Double>
二、运行时类型查询只适用于原始类型
三、不能建立参数化类型的数组
四、不能实例化类型变量
五、泛型类的静态上下文中类型变量无效
好比:private static T single; // ERROR
六、不能抛出或捕获泛型类的实例

5、通配符类型


一、限定上界通配符

好比:List<? extends Animal>,表示任何泛型List类型,它的类型参数是Animal类及子类,List<Animal>、List<Cat>、List<Dog>都是List<? extends Animal>子类型。
public static void main(String[] args)
{
    List<? extends Animate> animates = new ArrayList<Animate>();         // OK
    List<? extends Animate> animates1 = new ArrayList<Cat>();            // OK
    List<Animate> animates2 = new ArrayList<Animate>();                  // OK
    List<Animate> animates3 = new ArrayList<Cat>();                      // compile-time error       
}
其实,咱们能够将List<? extends Animate> animates 看作是List<Animate>、List<Cat>等的集合。
概括:假如给定的泛型类型为G,两个具体的泛型参数X、Y,当Y是X的子类时(Y extends X)
  • G<? extends Y> 是 G<? extends X>的子类型(如List<? extends Cat> 是 List<? extends Animal>的子类型)。
  • G<X> 是 G<? extends X>的子类型(如List<Animal> 是 List<? extends Animal>的子类型)
  • G<?> 与 G<? extends Object>等同,如List<?> 与List<? extends Objext>等同

学到这里,可能会遇到一些疑惑的地方,或者说事理解不透的地方,先观察以下两段代码片断,判断一下其是否可行??
List<? extends Animal> animal = new ArrayList<>();
animal.add(new Animal());
animal.add(new Cat());
上面的两个add操做都不能经过编译。为何呢?因为List:add(E e)加入泛型变成List<? extends Animal>:add(? extends Animal e),? extends Animal参数类型没法肯定,能够是Animal、Cat等,因此为了保护其类型的一致性,所以不容许向list对象中添加任意对象,除了null。
 
注意:上界限定通常用在:? extends T get()方法上(读数据操做)
 

二、限定下界通配符

好比:List<? super Cat>,这个通配符限制为Cat的全部超类型(包括本类)。
它的概括方法与上界通配符类似,这里就不啰嗦了。
看下面代码:
List<? super Animate> animates = new ArrayList<>();    
animates.add(new Animate());
animates.add(new Cat());
上述代码编译经过,编译器不知道add方法的确切类型,可是能够用任意Animal对象(或子类型对象)。
 
注意:下界通配符一般用于:set(? extends T>)方法上(写入数据操做)

三、无限定通配符

好比:List<?>,当类型不肯定时,才使用,该通配符较少使用。
 
关于上下界限定通配符,建议参考:《编写高质量代码:改善java程序的151个建议》中建议96:不一样的场景使用不一样的泛型通配符
 
参考

一、Java 泛型通配符?解惑

相关文章
相关标签/搜索