泛型,通配符

泛型

泛型的意义:

1.可以对类型进行自动检查。并不是替换,而是在编译期间进行检查
2.自动对类型进行转换。

泛型到底是怎么编译的?

类型的擦除机制===》向上擦除===》Object类
在编译器编译期间,把泛型全部擦除为Object类型

如我们可以写一个泛型栈如下:
其中T 只是一个类型占位符 表示GenericStack是一个泛型类

class GenericStack<T> {
    private T[] elem;
    private int top;


    public GenericStack() {
        this.elem = (T[]) new Object[10];
        this.top = 0;
    }

    public void push(T val) {
        this.elem[this.top++] = val;
    }

    public void pop1() {
        this.top--;
    }

    public T getTop() {
        return this.elem[this.top - 1];
    }
}

接下来写main函数调用该栈

public class GenericStackDemo {

    public static void main(String[] args) {
        GenericStack<Integer> genericStack = new GenericStack<Integer>();
        genericStack.push(10);
        genericStack.push(12);
        int top1 = genericStack.getTop();
        GenericStack<String> genericStack1 = new GenericStack<String>();
        genericStack1.push("a");
        genericStack1.push("b");
        String top2 = genericStack1.getTop();
        System.out.println(top1);//12
        System.out.println(top2);//b
    }
}

在调用栈中的getTop()方法时,泛型栈就会自动识别栈顶元素的类型,不用再加强转符号

泛型中存在以下几点需要注意

  • 不能new泛型类型的数组 this.elem = new T[]; Error
  • 不能new泛型类型的对象 T obj = new T();
  • 不能new泛型类型的对象数组
  • 不能用简单类型作为泛型的参数
  • GenericStack genericStack = new GenericStack(); 一定记得在类名后加</泛型类型的参数>,否则就是Object
  • 在static方法中,不能使用泛型类型的参数,因为:static方法不依赖对象

内存泄漏问题

如果用刚才的泛型栈,如果栈中存放的是对象类型的数据,在调用出栈方法时,会造成内存泄漏问题

    public void pop1() {
        this.top--;
    }

从代码可以看出,此种方法出栈只是将栈顶指针下移,而对象还是存在的,JAVA中的GC回收器只会自动回收没有没有被引用的对象,可做如下修改

    public void pop2() {
        this.elem[top-1] = null;
        --this.top;
    }

这样出栈之后刚才的对象就不在被引用,就会被回收

我们可以用用jmap命令查看内存泄漏,
jps ===>查看java进程号
jmap -histo:live 进程号 > d:\log.txt(查看对象个数要存放的文件夹)
start e:

将main函数修改如下,存放Animal对象类型的成员

    public static void main(String[] args) throws InterruptedException {
        GenericStack<Animal> genericStack = new GenericStack<Animal>();
        genericStack.push(new Animal());
        genericStack.push(new Animal());
        genericStack.push(new Animal());
        genericStack.pop();
        System.gc();
        Thread.sleep(100000);
    }

用pop1()方法出栈,仍存在三个Animal类的对象
用pop2()方法出栈,存在两个Animal类的对象,证明有一个对象被GC回收器自动回收

泛型的上界 T extends Comparable

class GenericAlg<T extends Comparable<T>> {
    public T findMaxVal(T[] array) {
        T max = array[0];
        for (int i = 1; i < array.length; i++) {
            if (array[i].compareTo(max) > 0) {
                max = array[i];
            }
        }
        return max;
    }
}
public class GenericDemo {
       public static void main1(String[] args) {
        Integer[] array = {1, 2, 3, 4, 5, 6, 7};
        Double[] array1 = {1.0, 2.2, 6.3, 5.2};
        GenericAlg<Integer> genericAlg = new GenericAlg<Integer>();
        System.out.println(genericAlg.findMaxVal(array));//7
        GenericAlg<Double> genericAlg1 = new GenericAlg<Double>();
        System.out.println(genericAlg1.findMaxVal(array1));//6.3
    }
}

如上代码是求数组中的最大值,在GenericAlg类中加上<T extends Comparable>,因为T是向上擦除直到Object,因为我们要用到compareTo方法,所以我们规定一个上界为Comparable接口,就可以调用此方法,进行比较

但我们会发现,用此方法进行比较,每种类型的数组都需要新建一个对象去调用类中的findMaxVal方法,所以我们可以将该方法写成静态方法,如下

注意写静态方法时,泛型的上界写在静态方法修饰符后

class GenericAlg2 {
    public static <T extends Comparable<T>> T findMaxVal(T[] array) {
        T max = array[0];
        for (int i = 1; i < array.length; i++) {
            if (array[i].compareTo(max) > 0) {
                max = array[i];
            }
        }
        return max;
    }
}
public class GenericDemo {
    public static void main(String[] args) {
        Integer[] array = {1, 2, 3, 4, 5, 6, 7};
        Double[] array1 = {1.0, 2.2, 6.3, 5.2};
        //T会通过实参的类型推演出泛型类型
        System.out.println(GenericAlg2.findMaxVal(array));//7
        System.out.println(GenericAlg2.findMaxVal(array1));//6.3
    }
}

这样就可以写任意类型的数组,用类调用求最大值的方法

通配符 “?”

通配符"?" 的擦除机制,会擦除到Object类

写一个函数,打印集合ArrayList中的全部元素

class GenericAlg3 {
    /*如果写成ArrayList<T> List,会擦除到Object类
    而ArrayList<Object>与ArrayList<Integer>不构成继承关系,所以在编译时无法识别
    因此要写成ArrayList<?> List,用通配符识别,则可以转换成任意类型
     */
    public static void printList(ArrayList<?> List) {
        for (Object obj : List) {
            System.out.println(obj + " ");
        }
    }
}

public class GenericDemo1 {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(10);
        arrayList.add(12);       
        arrayList1.add(14);
        arrayList1.add(16);
        GenericAlg3.printList(arrayList);            
    }
}

通配符的下界 <? super T>

找到是不是有T的基类实现了Comparable接口
主要用来读取

例如下面的代码
在代码中,Student继承了Person类,而Person实现了Comparable接口

在GenericAlg4类中的静态方法findMaxVal1方法中通配符规定了下界,所以编译时会自动寻找是否存在基类实现了Comparable接口,Person类作为基类实现了Comparable接口,所以其派生类就不用再实现Comparable接口

class Person implements Comparable<Person>{
    private String name;
    public Person(String name) {
        this.name = name;
    }

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

    @Override
    public int compareTo(Person o) {
        return name.compareTo(o.name);
    }
}
class Student extends Person {
    private int age;
    public Student(String name,int age) {
        super(name);
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                '}';
    }
}
class GenericAlg4 {
    public static <T extends Comparable<? super T>> T findMaxVal1(ArrayList<T> list) {
        T max = list.get(0);
        for (int i = 0; i < list.size(); i++) {
            if(max.compareTo(list.get(i)) < 0) {
                max = list.get(i);
            }
        }
        return max;
    }

public class GenericDemo2 {
    public static void main(String[] args) {
        ArrayList<Person> arrayList1 = new ArrayList<Person>();
        arrayList1.add(new Person("Allen"));
        arrayList1.add(new Person("DQ"));
        System.out.println(GenericAlg4.findMaxVal1(arrayList1));

        ArrayList<Student> arrayList2 = new ArrayList<Student>();
        arrayList2.add(new Student("Allen",20));
        arrayList2.add(new Student("DQ",15));
        System.out.println(GenericAlg4.findMaxVal1(arrayList2));
    }
}

通配符的上界 ArrayList<? extends T>

上界主要用来写入 一般用作库的开发
我们仍然用在解释下界时所使用的代码,将其中的findMaxVal方法修改如下:

public static <T extends Comparable<T>> T findMaxVa2(ArrayList<? extends T> list) {
        T max = list.get(0);
        for (int i = 0; i < list.size(); i++) {
            if(max.compareTo(list.get(i)) < 0) {
                max = list.get(i);
            }
        }
        return max;
    }

猜你喜欢

转载自blog.csdn.net/weixin_43289802/article/details/84257315