java语言基础2--泛型

  泛型特性出来之前,大家都使用Object类型来创建一般化的类,但是它们不能以安全的方式进行工作,泛型可以安全的工作,因为不再需要显式的强制转换,此外泛型还可以扩展重用代码的能力。

基本知识

  下面是一个简单的泛型例子

public class Gen<T> {
    T val;
    
    Gen(T t){ val = t;}
    
    public T getVal() { return val; }

    void showType() {
        System.out.println(val.getClass().getName());
    }
}
public class Test {
    public static void main(String[] args) {
        //T 类型将被指定为Integer 类型。实际上编译器并没有创建不同版本的Gen类,编译器只是移除泛型所有泛型信息,进行了类型替换,这个过程叫泛型擦除
        Gen<Integer> gen1 = new Gen<Integer>(100);
        gen1.showType();//输出 java.lang.Integer
        System.out.println(gen1.getVal());//输出100
        
//        gen1 = new Gen<Double>(10.0);//上面已经将gen1 指定为了Gen<Integer>类型了,不能再指定为Gen<Double> 这种检查是泛型的特点之一,保证了类型的安全
        
        //T 类型将被指定为String 类型
        Gen<String> gen2 = new Gen<String>("I am a String");
        gen2.showType();//输出java.lang.String
        System.out.println(gen2.getVal());//输出 I am a String
    }
}

有界泛型 语法<T extends superClass> , 或者<T extends superClass & myInterface>  这里的接口也使用extends关键字来修饰

  T能传递所有类型,这当然很好,但有时候,我们需要做一个限定,比如当我们想对一组数据求平和,那么类型肯定要数值类型才行的。因此我们需要做限定。

public class Stats<T extends Number> {
    T[] nums;
    
    Stats(T[] param){this.nums=param;}
    
    double average() {
        double sum = 0.0;
        for(int i = 0;i<nums.length;i++) {
            sum +=nums[i].doubleValue();
        }
        return sum;
    }
}
public class Test {
    public static void main(String[] args) {
        Integer [] arr = {1,2,3,4,5};
        Stats<Integer> s = new Stats<Integer>(arr);
        System.out.println(s.average());//输出 15.0
        
        BigDecimal [] arr1 = {new BigDecimal("1.1"),new BigDecimal("2.2"),new BigDecimal("3.3"),new BigDecimal("4.4"),new BigDecimal("5.5")};
        Stats<BigDecimal> s1 = new Stats<BigDecimal>(arr1);
        System.out.println(s1.average());//输出16.5
          
        String[] arr2 = {"1.1","2.2"};
//        Stats<String> s2 = new Stats<String>(arr2);//String  不是Number类型 ,因此编译失败
    }
}


 通配符 ?

  有时候我们有以下需求,比较两个数组的求和是否相等,Integer[] arrr1={1,2,3} 和 Double arr2={1.0,2.0,3.0};期望返回true;于是我们可能会像下面这样写

public class Stats<T extends Number> {
    T[] nums;
    
    Stats(T[] param){this.nums=param;}
    
    double average() {
        double sum = 0.0;
        for(int i = 0;i<nums.length;i++) {
            sum +=nums[i].doubleValue();
        }
        return sum;
    }
    
    boolean sameAverage(Stats<T> t) {
        return this.average()==t.average();
    }
}
public class Test {
    public static void main(String[] args) {
        Integer [] arr = {1,2,3,4,5};
        Stats<Integer> s = new Stats<Integer>(arr);
        System.out.println(s.average());//输出 15.0
        
        BigDecimal [] arr1 = {new BigDecimal("1.1"),new BigDecimal("2.2"),new BigDecimal("3.3"),new BigDecimal("4.4"),new BigDecimal("5.5")};
        Stats<BigDecimal> s1 = new Stats<BigDecimal>(arr1);
        System.out.println(s1.average());//输出16.5
          
        if(s.sameAverage(s1.average())) {//这里会编译失败 ,因为调用者是Stats<Integer>类型的 ,而比较对象是Stats<BigDecimal>,类型不兼容,导致编译失败
            System.out.println("求和相同");
        }else {
            System.out.println("求和不同");
        }
    }
}

 为了将 sameAverage方法 泛型化,我们需要使用通配符?,表示未知类型,修改以上代码,即可通过编译

    boolean sameAverage(Stats<?> t) {//这里 Stats<?>和所有Stats对象匹配
        return this.average()==t.average();
    }

有界通配符  语法<? extneds superClass> 上界,<? super subClass>  下界(注意与<T extneds superClass>的区别)

 泛型方法

public class Generic {
    static <T extends Comparable<T>,V extends T> boolean isIn(T x,V[] arr) {
        boolean flag = false;
        for(int i=0;i<arr.length;i++) {
            if(x.equals(arr[i])) {
                flag=true;
                break;
            }
        }
        return flag;
    }
    public static void main(String[] args) {
        Integer[] arr1 = {1,2,3,4};
        System.out.println(isIn(3, arr1));//输出true
        
        String[] arr2 = {"a","b","c"};
        System.out.println(isIn("a", arr2));//输出true
    }
}
static <T extends Comparable<T>,V extends T> boolean isIn(T x,V[] arr)
泛型方法 语法格式是将参数置于返回类型之前,这里是T继承Comparable<T>,同时V extends T的限制,返回类型前的<>的内容仅仅是对类型做一个限制

也可以将构造方法泛型化
public class GenCons {
    private double val;

    <T extends Number> GenCons(T t) {
        this.val = t.doubleValue();
    }

    public double getVal() {
        return val;
    }

    public void setVal(double val) {
        this.val = val;
    }
    
    public static void main(String[] args) {
         GenCons con1 = new GenCons(100);
         System.out.println(con1.getVal());
         
         GenCons con2 = new GenCons(100.99);
         System.out.println(con2.getVal());
    }
}

  泛型接口,

public interface  MinMax<T extends Comparable<T>> {
    T min();
    T max();
}
public class MyClass<T extends Comparable<T>>  implements MinMax<T> {
    T[] vals;
    MyClass(T[] vals){
        this.vals=vals;
    }

    @Override
    public T min() {
        T min = vals[0];
        for(int i=0;i<vals.length;i++) {
            if(vals[i].compareTo(min)<0) {
                min=vals[i];
            }
        }
        return min;
    }

    @Override
    public T max() {
        T max = vals[0];
        for(int i=0;i<vals.length;i++) {
            if(vals[i].compareTo(max)>0) {
                max=vals[i];
            }
        }
        return max;
    }
    
    public static void main(String[] args) {
        Integer[] arr1 = {1,2,3,4};
        MyClass<Integer> m1 = new MyClass<>(arr1);
        System.out.println(m1.min());//输出 1
        System.out.println(m1.max());//输出 4
        
        String[] arr2 = {"a","b","c","d"};
        MyClass<String> m2 = new MyClass<>(arr2);
        System.out.println(m2.min());//输出  a
        System.out.println(m2.max());//输出  d
    }
}

  看一下泛型接口的声明interface   MinMax<T extends Comparable<T>>  和普通类差不多

  看一下实现类class MyClass<T extends Comparable<T>> implements MinMax<T>,因为MyClass实现MinMax 所以界限和接口的界限一致,此外这个界限不需要在implements 子句中指定,参数可以原封不动的传递给接口,一般来说 一个类实现泛型接口,那么这个类也需要是泛型化的比如以下方式就是错误的

public class MyClass implements MinMax<T>//错误 T类型未知

  因为没有声明参数,所以无法传递类型参数给接口,编译器会报 T类型未知的错误

  当然,如果实现的是一个具体的泛型接口,那么类是无需泛型化的,如以下是合法的

public class MyClass implements MinMax<Integer> 

   泛型类的继承的时候,也可以指定为子泛型类设置多个参数,如下

public class Gen<T> {
    T ob;

    public T getOb() {
        return ob;
    }

    public void setOb(T ob) {
        this.ob = ob;
    }

}
//这里的T 将传递给Gen,V将作为自己的类型
public class Gen2<T,V> extends Gen<T> {
    V v;

    public Gen2(V v) {
        super();
        this.v = v;
    }
}

 非泛型类也可以作为泛型类的父类 如下

class Gen<T> extends  NonGen{
}

使用instanceof 在运行时对泛型进行类型判断

public class Gen<T> {
    T ob;
    public Gen(T ob) {
        super();
        this.ob = ob;
    }

    public T getOb() {
        return ob;
    }
}
public class Gen2<T> extends Gen<T> {

    public Gen2(T o) {
        super(o);
    }
    
    public static void main(String[] args) {
        Gen<Integer> g1 = new Gen<Integer>(10);
        System.out.println(g1 instanceof Gen<?>);//输出true
        
        Gen2<Integer> g2 = new Gen2<Integer>(20);
        System.out.println(g2 instanceof Gen<?>);//输出true
//        System.out.println(g2 instanceof Gen2<Integer>);//编译失败 不能讲g2 与特定的类型比较
        
        Gen<Integer> test = (Gen<Integer>) g2;//也可以对泛型进行强制转换
        
        Gen<String> g3 = new Gen<String>("Hello");
        System.out.println(g3 instanceof Gen2<?>);//输出fasle
    }
}

泛型擦除:编译java代码时,所有的泛型信息将被移除,意味着使用它们的界定类型替换类型参数,如果没有显式的指定类型,将使用Object代替。

使用泛型的注意事项

public class Gen<T> {
//    static T test;  // 不能静态成员不能使用 泛型参数
    T ob;
    public Gen(T ob) {
//        ob = new T();  // T只是占位符  不能实例化, 因为jvm不知道T是哪种类型
    }

    public T getOb() {
        return ob;
    }
}

暗示

原文地址:https://www.cnblogs.com/tjqBlog/p/9778531.html