泛型学习

Posted 巴拉巴拉顺

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了泛型学习相关的知识,希望对你有一定的参考价值。

1.泛型定义:就是允许定义类、接口、方法时使用类型参数。这个类型参数将在声明变量、创建对象、调用方法时动态指定(实际类型)。

public class Apple<T>{
    private T info; //T 可以拿来当一个类型使用,如String,int等。可以在真实使用Aplle类的时候,再指定。
    
    public Apple(){} // 为带泛型的类,定义构造器时,不用增加泛型类型,但是在调用构造器时,可以使用Apple<T>形式。因为在JAVA7提供了菱形4语法,允许省略<>中的实际类型
    
    public void setInfo(T info){
        this.info = info;
    }
}
public interface Apple<T>{ // 定义泛型接口 }

 
2.从泛型派生子类
        当创建了带泛型的接口或类后,可以为该接口创建实现类或从该父类派生子类。但是,使用这些父类及接口时,不能再包含类型参数。如下面的代码就是错误的:

1 public class A extends Apple<T>{}    //    错误,此时T必须制定实际的类型参数或者省略,默认的Object

也就是说,在定义类、接口、方法时可以声明类型参数,但在使用这些带类型参数的接口、类、方法时,必须为该类型参数传入实际的类型。
 所以上面的代码应该改成:

public class A extends Apple<String>{
    // 但是注意,重写父类方法时,这里的方法参数类型也要变成String了
    public void setInfo(String info){
        this.info = info;
    }
}

但是,使用带泛型的父类和接口时,参数类型可以省略,如下也是正确的:

// 此时没有传入实际的,JAVA编译器可能会告警:使用了不检查或不安全的操作。此时,系统默认把省略的T当成Object处理
public class A extends Apple{ 
    private T info;
    // 这里就是Object了
    public void setInfo(Object info){
        this.info = info;
    }
}


3.并不存在的泛型类型
    有人可能会把ArrayList<String> 当成ArrayList的子类。实际上,JVM并没有为ArrayList<String>生成任何新的class文件。也就是说ArrayList<String>和ArrayList它们在JVM中运行的都是同一个class文件。在内存中也都占用一块内存。因此在静态方法,静态初始化块或静态变量的声明中是不允许使用类型参数。

4.类型通配符
    假如我们有一段逻辑,需要遍历List中的元素,但是List中对象类型,我们未知。

public void test(List c){
    for(int i = o; i< c.size(); i++){
        System.out.println(c.get(i));
    }
}

 List是一个带泛型的对象,使用时,必须指定类型,否则会引起警告.假如,我们改成如下:

Public void test(List<Object> c){
    for(int i = o; i< c.size(); i++){
        System.out.println(c.get(i));
    }
}

程序改成这样后,代码虽然不会引入警告。但是假如我们传入一个List<String>类型的参数,程序会报错。因为List<String> 并不是List<Object>的子类
在这种情况下,我就可以是类型通配符(?):

Public void test(List<?> c){ // 此时,List<?>,可以理解为,未知类型元素的List。 那么就可以传入任意类型的List对象了
    for(int i = o; i< c.size(); i++){
        System.out.println(c.get(i));
    }
}

 需要注意的是,这种写法仅仅标识它是各种泛型List的父类,不能往这个List中添加元素,因为不知道List<?>的实际的元素类型。就好此,你声明了一个List<E>,往这个List中添加的元素,必定是E或者是它的子类,其元素的类型是确定的。但"?"不行,“?”代表的就是一个位置类型,往集合增加元素时,必须明确其类型
    另一方面,List<?>使用get()方法时,取出来的其实也是一个未知类型,但它总是Object对象或其子类。因此,从这种角度来说 ,get出来的元素类型也是确定的。
    
类型通配符的上限:
        使用List<?>标识,标识任何泛型LIst的父类。有时候,我们想要限定这个范围,如我们想要这个List只放动物类,如:

public abstract class Animal{ // 定义一个抽象父类 动物
}

public class Dog extends Animal{} // 定义一个子类 狗

public class Dat extends Animal{} // 定义一个子类 猫

public class Apple {} // 定义一个水果类,苹果

 此时我们可以声明一个指允许添加animal的List:

public void test(List<? extends Animal> animals){ // 此时,传入一个List<Apple> 就会报错
    for(int i=0; i < animals.size(); i++ ){
        animals.get(i);
    }
}    

这里需要说明的时,List<? extends animal>是受限制通配符的例子,此处的?代表的一个未知的类型,因此也不能往这个List中添加元素。  但是此时的?这个未知类型,我们知道肯定是animal或它的子类,因此我们把animal称为这个通配符的上线。
        
 我们也可以在定义类型参数时,使用通配符设定上限(就是创建带泛型的类和接口时,设定上线)。如下:

public class Animal<T extends Number>{} // 在使用T时,传入的元素类型,就必须是Number的子类或其本身了

 在极端情况,程序可以为类型形参设定多个上线(之多一个父类上限(放第一个),可以有多个接口),表明该类型是其父类的子类(或本身),同时要实现多个上限接口,如:

public class Animal<T extends Number && java.io.Serializable>{} // 这里就要求,T类型必须是Number的子类且实现了Serializable接口

 
5. 泛型方法:
        我们需要实现一个功能,将一个集合的元素,复制到一个集合中去:

public void test(Object[] a,Collection<Object> c){
    for(Object o:a){
        c.add(o);
    }
}

 上述这个可以正常运行,但是方法,限制很大,Collection<Object>形参只是能是Collection<Object>,因为Collection<String>不是其子类。如果使用Collection<?>,显然也是不行的,?代表一个未知类型,不能把对象放进一个未知类型的集合中。为了解决这个问题,java5中引入了泛型方法,语法格式:

修饰符 <T,S> 返回值类型 方法名(形参列表){
    //。。。
}

将上述的方法可以改成:

public <T> void test(T[] a,Collection<T> c){
    for(T o:a){
        c.add(o);
    }
}

此时,假如我们调用下面的程序来调用test方法:

public static void main(Stirng[] agrs){
    String[] a = {"1","2"};
    List<Object> c = new ArrayList<>();
    test(a,c); // 会报错,要求连个参数的必须是相同的,但是实际传入的一个是String,一个是Object
}

这里我们可以使用通配符来修改程序:

public <T> void test(<? extends T>[] a,Collection<T> c){ // 此时,第一个参数只要是T的子类就可以了
    for(T o:a){
        c.add(o);
    }
}


    通配符的下限:
        <? extends T> ? 通配符为T的子类,T称为通配符的上限。反之,当我们标识一个泛型的父类,可以使用 <? super T>来标识。?通配符表示T的父类类型(或其本身类型)。此时,T称为通配符的下限

public <T> void test(<? super T>[] a,Collection<T> c){ // 此时,只要是T的父类即可
    for(T o:a){
        c.add(o);
    }
}

 

以上是关于泛型学习的主要内容,如果未能解决你的问题,请参考以下文章

201621123062《java程序设计》第九周作业总结

201621123037 《Java程序设计》第9周学习总结

操作 Java 泛型:泛型在继承方面体现与通配符使用

这个嵌套类构造函数片段可以应用于泛型类吗?

201621123054《Java程序设计》第九周学习总结

Java泛型:类型擦除