泛型
我们先来看一下代码,这是一个用泛型实现的栈,下面的 T 就是代表了这是个泛型,它是一个占位符,可以指定任何类型。
我们先看一下泛型有什么优点。比如我们要写一个栈,一般来说,栈的存储类型是固定了的,当我们需要存储其他类型的元素时,我们只能修改代码,但是使用泛型我们完全不用考虑这点,因为用泛型写的栈存储的元素随着我们传入的参数不同而不同。
class GenericStack<T>{ private T[] elem;//声明一个 T 类型的数组,但是 T 现在不确定 private int top; public GenericStack() { this(10); } public GenericStack(int size) { this.elem = (T[])new Object[size]; this.top = 0; } public void push(T val){ this.elem[this.top++] = val; } public void pop(){ --this.top; } public T getTop(){ return this.elem[this.top-1]; } }
public static void main(String[] args) { GenericStack<Integer> stack = new GenericStack<Integer>(); stack.push(10); stack.push(20); stack.push(200); int a = stack.getTop(); System.out.println(a); }
输出结果是 200
泛型的意义
对类型进行自动检查
对类型进行自动转换
泛型的坑
不能 new 泛型类型的数组。
不能 new 泛型类型的对象。
不能得到泛型类型的对象数组。
简单类型不能作为泛型类型的参数。
泛型是如何编译的
泛型在编译期间通过它的类型擦除机制生成一个 Object 对象。以为就是说泛型的参数类型在编译期间被编译器擦除了,在运行期间 JVM 得到的是一个 Object 对象。类型擦除机制是向上擦除的,也就是往基类方向擦除。
泛型的类型擦除机制
泛型的上界
因为泛型的类型擦除机制,最后在运行期间我们得到的是一个 Object 类型的值,在有的情况下不是很方便,所以我们需要指定一下我们类型擦除机制向上擦除到什么地方位置,这就引出了泛型的上界。
class GenericAlg<T extends Comparable<T>>{ public T findMaxVal(T[] array){ T maxVal =array[0]; for(int i = 1;i < array.length;i++){ if(maxVal.compareTo(array[i]) < 0){ maxVal = array[i]; } } return maxVal; } public static void main(String[] args) { Integer[] array = {10,20,30}; GenericAlg<Integer> g1 = new GenericAlg<Integer>(); System.out.println(g1.findMaxVal(array)); } }
输出结果:30
在这个例子中我们指出了上界为 Comparable,所以我们可以直接调用它里面的方法。
泛型方法
class Usr implements Comparable<Usr>{ private String name; private String sex; private int age; public Usr(String name,String sex,int age) { super(); this.name = name; this.sex = sex; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Usr [name=" + name + ", sex=" + sex + ", age=" + age + "]"; } @Override public int compareTo(Usr o) { return age > o.age ? 1 : (age == o.age) ? 0 : -1; } } class Gnericl{ /** * * @param array * @return * 泛型方法 */ public static<T extends Comparable> T fingMaxVal(T[] array){ T maxVal =array[0]; for(int i = 1;i < array.length;i++){ if(maxVal .compareTo(array[i]) < 0){ maxVal = array[i]; } } return maxVal; } public static void main2(String[] args) { Usr[] usr = new Usr[3];//定义一个 Usr 类型的数组 usr[0] = new Usr("洁1","男",17); usr[1] = new Usr("洁3","男",19); usr[2] = new Usr("洁2","男",21); System.out.println(); System.out.println(Gnericl.findMaxVal(usr)); } }
内存泄漏
看下面的代码,我们创建了三个对象放入栈里,然后又出栈了一个元素,所以应该是两个对象,但经过测试,我们应该得到了三个对象,这就是内存的
public static void main(String[] args) { GenericStack<Animal3> s = new GenericStack(); s.push(new Animal3()); s.push(new Animal3()); s.push(new Animal3()); s.pop(); System.gc();//测试内存泄漏 }
现在我们给出战的方法里将即将出栈的元素置为 null ,从结果来看我们成功的防止了内存的泄漏。
public void pop(){ this.elem[top-1] = null; --this.top; }
通配符
表示当前类型可以是任何类型。
它也会进行类型擦除的 擦除到Object.
通配符分类
无界通配:?
通配符的上界:? extends Object
通配符的下界:? super Integer