泛型,通配符
泛型
泛型的意义:
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类中加上
但我们会发现,用此方法进行比较,每种类型的数组都需要新建一个对象去调用类中的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;
}
还没有评论,来说两句吧...