泛型使用及堆污染

133 阅读2分钟

Java泛型是J2 SE1.5引入的特性,本质上是参数化类型,可以用于类、接口、方法的创建中。

泛型的使用

//泛型定义在类
public class Generics<T> {
    //实例方法使用类定义泛型
    public T test1(T t){
        return null;
    }
    //方法定义泛型
    public <K> K test2(){
        return null;
    }
}

下面看个错误例子

public static ~~V~~ test3(){ return null; }   

静态方法使用类上定义的泛型会出错,泛型是在对象创建之后获取,static修饰的是类级别的,加载静态方法时无法获取到泛型。
静态方法可以使用方法级别定义泛型

public static <P> P test4(){ return null;}

接口定义泛型

interface GenericInf<T>{
   T add(T a,T b);
   T sub(T a,T b);
   T mul(T a,T b);
   T div(T a,T b);
}

接口泛型实现案例

class DoubleCalc<Double> implements GenericInf<Double>{

    @Override
    public Double add(Double a, Double b) {
        return null;
    }

    @Override
    public Double sub(Double a, Double b) {
        return null;
    }

    @Override
    public Double mul(Double a, Double b) {
        return null;
    }

    @Override
    public Double div(Double a, Double b) {
        return null;
    }
}

泛型通配符

  • 无界通配符 ?
//接受所有类型
public void border1(ArrayList<?> list){}
  • 上界通配符 ? extends Number
// 只允许Number 及Number的子类型  ? extends Object等效于无界
public void border2(ArrayList<? extends Number> list){}
  • 下界通配符 ? super Integer
//指定的类型
public void border3(ArrayList<? super Integer> list){}

泛型作用

  • 编译类型检查,保证类型安全
  • 避免类型转换硬编码
  • 提高代码重用性

泛型的本质

泛型不会影响数据的基本类型。在java中泛型是伪泛型,java编译期间会将泛型擦除

public void check1(){
    List list = new ArrayList();
    List<String> stringList = new ArrayList<String>();
    System.out.println(list.getClass() == stringList.getClass());//true
}

验证泛型的编译检查

public void check2() throws Exception{
 // 仅在编译期进行类型检查
 List<String> list = new ArrayList<>();
 list.add("a");
 list.add("b");
 System.out.println(list.size());//2
 
 //当利用反射在运行期间添加元素时 也改变了list的长度 
 Class<? extends List> clazz = list.getClass();
 Method m = clazz.getDeclaredMethod("add", Object.class);
 m.invoke(list, new Object());
 
 System.out.println(list.size());//3 
}

需要注意的是,由于java泛型擦除的特征,当一个可变泛型的参数指向一个无泛型的参数可能会产生堆污染。

class population{
    public static void main(String[] args) {
        Set set = new TreeSet();
        populate(set);// 没有泛型的set传入到 populate方法中已经发生了堆污染
        set.add("abc");// 因此在添加字符串时会报错
    }

    private static void populate(Set<Integer> set) {
        set.add(new Integer(123));
        System.out.println(set.size());
    }
}
1
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
	at java.lang.String.compareTo(String.java:111)
	at java.util.TreeMap.put(TreeMap.java:568)
	at java.util.TreeSet.add(TreeSet.java:255)
	at com.zld.cloud.population.main(Generics.java:67)

泛型擦除 对污染问题发生

interface ParentClass<T>{
    T customerDefine(T t);
}
class ChildClass implements ParentClass<String> {

    @Override
    public String customerDefine(String s) {
        return "看看是啥:"+s;
    }
}
class BridgeMethod{
    public static void main(String[] args) {
        ParentClass childClass = new ChildClass();//父类的引用指向子类的实现 多态
        //这里调用的时子类方法
        System.out.println(childClass.customerDefine("12321"));
        //这里调用的时父类的方法
        System.out.println(childClass.customerDefine(new Object()));//编译通过 运行报错
    }
}