泛型与通配符

1.基本定义

泛型:在定义类的时候并不会设置类中的属性或方法中参数的具体类型,而是在使用类时候在进行定义。若是在使用时不规定具体类型,则默认按Object型处理,会警告但不会报错。

泛型的意义:

·会在编译期间对类型进行自动检查
·会自动进行类型转换

使用泛型需要注意的问题:

·不能new泛型类型的数组new T[]
·泛型的编译过程是对类型的擦除,将T擦除成Object类,而不是替换
·擦除机制:向上擦除,即向基类的方向擦除
·不能new泛型类型的对象,即: T t = new T();错误
·不能new泛型对象的数组,即 Object[] o = new Generic<Integer>[10];错误
·简单类型不能作为泛型的参数,因为简单类型没有基类,无法进行擦除操作
·泛型的上界:T extends 上界,泛型没有下界
·在static方法中不能用泛型类型的参数,因为static定义的方法不依赖于对象,无法指定泛型类型

2.泛型方法

泛型方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数类型,编译器适当地处理每一个方法的调用。泛型方法可以定义在泛型类或接口中,也可以单独定义。

定义泛型方法的规则:

    ·所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子
中的<T>)。
    ·每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于
指定一个泛型类型名称的标识符。
    ·类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
    ·泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)。

看以下例子:

/** 用一个数组实现不同的数据类型 */
public class Test{      
    // 在这里<T>只是一个泛型标记的声明               
   public static <T> void printArray(T[] Array){     
         for (T element : Array){        
            System.out.print(element + " ");
         }
         System.out.println();
    }

    public static void main( String args[] ){
        // 创建不同类型数组: Integer, Double 和 Character
        Integer[] intArray = { 1, 2, 3, 4, 5 };
        Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
        Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };

        System.out.println( "整型数组元素为:" );
        printArray( intArray  ); 

        System.out.println( "双精度型数组元素为:" );
        printArray( doubleArray ); 

        System.out.println( "字符型数组元素为:" );
        printArray( charArray );
    } 
}

程序的输出结果是:

        整型数组元素为:
        1 2 3 4 5 
        双精度型数组元素为:
        1.1 2.2 3.3 4.4 
        字符型数组元素为:
        H E L L O 

带界的类型参数:
有时候,我们需要限制那些被允许传递到一个类型参数的类型种类范围。例如,一个操作数字的方法可能只希望接受Number或者Number子类的实例。这就是有界类型参数的目的。
要声明一个有界的类型参数,首先列出类型参数的名称,后跟extends关键字,最后紧跟它的上界,即 T extends 上界,泛型没有下界。
看例子:

/** 求三个元素中最大的一个 */
public class Test{  
    /* Object类中没有comareTo()方法,此方法存在于Comparable接口中,因此
     * 向上擦除到此类就好,故设置上界为Comparable接口 
     */          
    public static <T extends Comparable<T>> T max(T x, T y, T z){                     
          T max = x;
          // 对象的比较用compareTo()方法,而不是大于小于号
          if ( y.compareTo( max ) > 0 ){
             max = y;
          }
          if ( z.compareTo( max ) > 0 ){
             max = z;          
          }
          return max;
       }
       public static void main( String args[] ){
          System.out.println(max(3, 4, 5));
          System.out.println(max( 6.6, 8.8, 7.7 ) );
          System.out.println(max( "你", "我", "他" ) );
       }
    }

结果是:

        5
        8.8
        我

3.泛型类/接口

和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
看例子:

/** 用泛型实现一个简单的栈 */
class GenericStack<T>{
    private T[] elem;
    private int top;
    public GenericStack(){
        this(10);
    }

    public GenericStack(int size){
        // 不能new泛型类型的数组new T[]
        this.elem = (T[])new Object[size];
        this.top = 0;
    }
    /** 元素压栈 */
    public void push(T val){
        this.elem[this.top++] = val;
    }
    /** 元素出栈 */
    public void pop(){
        // 元素出栈后该位置置为null,防止内存泄漏
        this.elem[this.top-1] = null;
        --this.top;
    }
    /** 获取栈顶元素 */
    public T getTop(){
        return this.elem[this.top - 1];
    }
}
public class Test{
    public static void main(String[] args) {
        GenericStack<Integer> s1 = new GenericStack<Integer>();
        s1.push(10);
        s1.push(20);
        int data = s1.getTop();
        System.out.println(data);
    } 
}

结果是:20


4.类型通配符

通配符多用于类库的开发,在Java源码里可以看到很多通配符的使用。通配符是用“?”代替具体的类型参数。例如 集合类接口List< ?> 在逻辑上是List、List 等所有List<具体类型实参>的父类。

·通配符也会进行类型的擦除,即向上擦除到Object类。
·通配符的上界:<? extends  T>,表示向上擦除的边界
·通配符的下界:<? super  T>,表示寻找是否有T类型的基类实现了Comparable接口。
即:
    < ? extends T> 表示该通配符所代表的类型是T类型的子类。
    < ? super T> 表示该通配符所代表的类型是T类型的父类。

阿里巴巴Java开发规约规定:

    泛型通配符< ? extends T >来接收返回的数据,此写法的泛型集合不能使用 add 方法,而 < ? super T> 不能使用 
get 方法,做为接口调用赋值时易出错。

说明:扩展说一下 PECS(Producer Extends Consumer Super) 原则:
    第一、频繁往外读取内容的,适合用<? extends T >。
    第二、经常往里插入的,适合用 <? super T> 。

看例子:

import java.util.*;

class GenericAlg {
     //下界:是否有T类型的基类实现了Comparable接口
     public static<T extends Comparable<? super T>> T findMaxPerson(ArrayList<T> list){
        T maxPerson = list.get(0);
        for(int i = 1;i < list.size();i++){
            if(maxPerson.compareTo(list.get(i)) < 0){
                maxPerson = list.get(i);
            }
        }
        return maxPerson;
    }
}
/** 继承了Comparable接口并重写了compareTo()方法,故Person类就是通配符的下界 */
class Person implements Comparable<Person>{
    private String name;
    public Person(String name) {
        super();
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + "]";
    }

    @Override
    public int compareTo(Person p) {
        return name.compareTo(p.name);
    }   
}
public class Test{
    public static void main(String[] args) {
        ArrayList<Person> list = new ArrayList<Person>();

        list.add(new Person("你"));
        list.add(new Person("我"));
        list.add(new Person("他"));
        System.out.println(GenericAlg.findMaxPerson(list));

    }
}

结果:Person [name=我]

猜你喜欢

转载自blog.csdn.net/qq_40178464/article/details/80531094