六、java泛型总结(设计背景\基本使用\堆污染\类型擦除)

1、 泛型的设计背景

参考《java编程思想4.0》中关于jdk 1.5 引入泛型的原因 , 有两点:

  • 1.1 多态有类型的范围限制,当想设计一个类,让其中用到的类型更通用时,多态无法满足。
    在java中,当我们为了让程序更易扩展、或者让代码更灵活,首先会考虑使用继承,或者接口进行设计(多态),但是这两种方式总会有一个使用范围限制。使用继承必须是基类的扩展类,使用接口则必须实现该接口。

附如下代码(继承方式),Test 类drawSth(Shape shape) 的入参必须是Shape的子类。

public abstract class Shape{

    public abstract void draw();

}

class  Circle extends  Shape{

    @Override
    public void draw() {
        System.out.println("a Circle");
    }
}
class  Triangle extends Shape{

    @Override
    public void draw() {
        System.out.println("a Triangle");
    }
}
class Test{

    public void drawSth(Shape shape){
        shape.draw();
    }

    public static void main(String[] args) {

        Test test = new Test();
        test.drawSth(new Circle()); //多态:向上造型
        test.drawSth(new Triangle());
    }

}
  • 1.2 容器技术,一个类持有大量对象时,方案更优雅(如集合ArrayList的设计)

2 、泛型的基本使用

  • 声明:使用一对 <> 声明,按使用位置分 泛型类、泛型方法、泛型接口
//泛型类 (也是对象持有技术的一种,另有继承,组合)
public class Test {
     private K  q;
     private V  q1;
     private H  q2;
    }

//泛型方法 
public    K  ddd(){
    return null;
}

//泛型接口
public interface Test {}
  • 通配符边界
    无界: eg: LIst
    上界: eg: List
    下界 : eg: List

3、使用注意

3.1 只是作用在方法上的话,尽量使用泛型方法,让读者更明确泛型的目的
3.2 声明在类上的泛型,类中静态方法无法使用,因为静态方法属于类级别,跟对象创建时机不一致。
3.3 注意堆污染
当一个持有泛型a的对象A,赋值给无泛型的对象B,再将B赋值给持有不同泛型的对象C,这时候取值会发生类型转换异常 : ClassCastException
如下代码:

public static void main(String[] args) {


        List strList = new ArrayList<>();
        strList.add("a");
        List integerList = new ArrayList<>();
        integerList.add(0);

        List list = integerList;

        List stringList = list;

        System.out.println(stringList.get(0));

    }

运行结果:


image.png

4、拓展

4.1 类型擦除
泛型只在编译器期作用,运行期把类型擦除了。
示例代码:List 对象如果想直接添加 Integer 类型的元素,会有一条红线提示编译不通过,但是通过反射在运行期间进行添加,却能添加成功。

  public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {


        List strList = new ArrayList<>();
        strList.add("a");
        System.out.println("编译期"+strList.size());
        //strList.add(123);  //编译器无法通过编译

        //使用反射进行运行期动态操作
        Class aClass = strList.getClass();
        Method add = aClass.getDeclaredMethod("add", Object.class);
        add.invoke(strList,123);
        System.out.println("运行期"+strList.size());


    }

运行结果:


image.png

Process finished with exit code 0
4.2 桥接方法
泛型是JDK 1.5 后的方法,编译成class文件后会自动生成一个桥接方法(出入参都是Object),兼容以前版本。

你可能感兴趣的:(六、java泛型总结(设计背景\基本使用\堆污染\类型擦除))