Java-泛型

2020-12-19  本文已影响0人  风拂吾心
- 1.泛型定义
- 2.泛型使用
- 3.泛型上下边界
- 4.JVM如何实现的泛型?

1、定义

Jdk1.5出现的新特性,主要是解决在编译期无法确定其类型,导致在运行期可能出现的强转错误,
泛型的本质是参数化类型,可以用形参替代实参,不同的参数类型可以执行相同的代码。

公共的数值计算方法,没有泛型的话代码是这样:

   public  int addSum(int t1, int t2) {
        return t1 + t2;
    }

    public  float addSum(float t1, float t2) {
        return t1 + t2;
    }

    public  double addSum(double t1, double t2) {
        return t1 + t2;
    }

有了泛型以后就变成了这样

    public <T extends Number> double addSum(T t1, T t2) {
        return t1.doubleValue() + t2.doubleValue();
    }

2、泛型使用

类型:
泛型类,泛型接口:<T>
泛型方法:<T>
通配符:<? extends T>,<? super T>

符号规范:
E - Element (在集合中使用,因为集合中存放的是元素)
T - Type(Java 类)
K - Key(键)
V - Value(值)
N - Number(数值类型)
? - 表示不确定的java类型
S、U、V - 2nd、3rd、4th types
注:java官方推荐写法,一种规范 见名知意便于大家阅读。

2.1、泛型类
/**
 * 泛型类
 * @param <T>
 */
public class XiaoMing <T> extends Person {

    private T vegetables;

    public XiaoMing() {
        name = "小明";
    }

    public T getVegetables() {
        return vegetables;
    }

    public void setVegetables(T t) {
        this.vegetables = t;
    }
}

public class Food {

    protected String name;
    protected String price;

    public static void main(String[] args) {
        /**
         * 没有使用泛型的情况下
         */
        Vegetables cabbage = new Cabbage();
        Vegetables lettuce = new Lettuce();

        XiaoMing xiaoMing = new XiaoMing();
        xiaoMing.setVegetables(cabbage);
        xiaoMing.setVegetables(new Object());

        /**
         * 使用泛型后
         */
        //指定类型 只能传入蔬菜类
        XiaoMing<Vegetables> xiaoMing2 = new XiaoMing();
        xiaoMing2.setVegetables(cabbage);
        //其他類型直接报错
 //     xiaoMing2.setVegetables(new Object());
    }
}
2.2、泛型接口

/**
 * 泛型接口
 * 做菜功能
 * @param <T>
 */
public interface Cooking<T> {
    /**
     * 炒菜
     * @param t 具体的食材
     */
    void stirFry(T t);

}

/**
 * 泛型接口实现类
 * 注:泛型接口的实现方法有二种
 * @param <T>
 * @param <T2>
 */

/**
 * 第一种:将类型指定放到使用期
 * @param <T>
 * @param <T2>
 */
public class XiaoMing <T,T2> extends Person implements Cooking<T2> {
/**
 * 第二种:直接在实现类指定其类型
 * @param <T>
 * @param <T2>
 */
//public class XiaoMing <T,T2> extends Person implements Cooking<Vegetables> {


    private T vegetables;
    private T2 dish;

    public XiaoMing() {
        name = "小明";
    }

    public T getVegetables() {
        return vegetables;
    }

    public void setVegetables(T t) {
        this.vegetables = t;
    }

    /**
     * 小明实现了{@link Cooking}接口,学会的做菜
     * @param t2
     */
    @Override
    public void stirFry(T2 t2) {
        System.out.println(name+"开始做菜...");
        System.out.println(name+"洗锅->放油->倒入"+t2+"->大火爆炒");
        System.out.println(name+"出锅->装盘");
    }
}


public class Food {

    protected String name;
    protected String price;

    public static void main(String[] args) {
        //蔬菜
        Vegetables cabbage = new Cabbage();
        Vegetables lettuce = new Lettuce();
        //水果
        Fruit apple = new Apple();

        /**
         * 没有使用泛型的情况下
         */
        XiaoMing xiaoMing = new XiaoMing();
        xiaoMing.stirFry(cabbage);
        //没有泛型的情况下,小明可以将apple下锅...5分钟后一份可口的清炒苹果就出锅了^_^
        xiaoMing.stirFry(apple);


        /**
         * 使用泛型后
         */
        //指定类型为蔬菜 水果不能下锅
        XiaoMing<Vegetables,Vegetables> xiaoMing2 = new XiaoMing();
        xiaoMing2.stirFry(cabbage);
        //水果类直接报错
   //     xiaoMing2.stirFry(apple);
  }
}
2.3、泛型方法

/**
 * 泛型方法
 * @param <T>
 * @param <T2>
 */
public class XiaoMing <T,T2> extends Person implements Cooking<Vegetables> {

    private T vegetables;
    private T2 dish;

    public XiaoMing() {
        name = "小明";
    }

  /**
     * 这不是泛型方法,只是恰好返回值为T,泛型方法的定义 
     * 必须加<>
     */
    public T getVegetables() {
        return vegetables;
    }

    public void setVegetables(T t) {
        this.vegetables = t;
    }

    /**
     * 泛型方法,并且指定T2类型为Vegetables
     * 小明实现了{@link Cooking}接口,学会的做菜
     * @param t2
     */
    @Override
    public <T2> T2 stirFry(T2 t2) {
        System.out.println(name+"开始做菜...");
        System.out.println(name+"洗锅->放油->倒入"+t2+"->大火爆炒");
        System.out.println(name+"出锅->装盘");
        return t2;
    }

    /**
     * 静态泛型方法
     * 小明享受美食
     * @param <T2>
     */
    public static <T2> void eatFood(T2 t){
        System.out.println(name+"开始享用"+t);
    }

    /**
     * 静态泛型方法
     * 小明加工美食
     * @param <T2>
     */
    public static <T> T workingFood(T... t){
        System.out.println(name+"开始加工"+t);
        System.out.println(name+"加工结束"+t);
        return t;
    }

}

/**
 * 泛型接口
 * 做菜功能
 * @param <T>
 */
public interface Cooking<T> {
    /**
     * 炒菜
     * @param t 具体的食材
     */
//    void stirFry(T t);

     <T2> T2 stirFry(T2 t2) ;
}

public class Food {

    protected String name;
    protected String price;

    public static void main(String[] args) {
    //蔬菜
        Vegetables cabbage = new Cabbage();
        Vegetables lettuce = new Lettuce();
        //水果
        Fruit apple = new Apple();
        Fruit orange = new Orange();
        /**
         * 没有使用泛型的情况下
         */
        XiaoMing xiaoMing = new XiaoMing();
        //开始炒菜->开吃
        XiaoMing .eatFood(xiaoMing.stirFry(cabbage));

        /**
         * 使用泛型后
         */
        //指定类型为蔬菜 水果不能下锅
        XiaoMing<Vegetables,Vegetables> xiaoMing2 = new XiaoMing();
        //开始炒菜->开吃
        XiaoMing .eatFood(xiaoMing2.stirFry(cabbage));
        //指定类型后依然可以放入水果 可以说明泛型方法完全独立于泛型类 泛型方法
        XiaoMing .eatFood(xiaoMing2.stirFry(apple));

       //食品加工
        XiaoMing .workingFood(cabbage ,lettuce ,apple,orange)
  }
}

3、泛型上下边界

3.1、extends关键字,泛型上界
/**
 * 泛型类
 * @param <T> 指定的类型必须继承自Vegetables类
 */
public class XiaoMing <T extends Vegetables> extends Person  {

    private T food;

    public XiaoMing() {
        name = "小明";
    }

    public T getFood() {
        return food;
    }

    public void setFood(T food) {
        this.food = food;
    }

    public  void eatFood(T t){
        System.out.println(name+"开始享用"+t);
        System.out.println();
    }
}

public class Food {

    protected String name;
    protected String price;

    public static void main(String[] args) {
        /**
         * extends关键字,泛型上界
         */
        //蔬菜
        Vegetables cabbage = new Cabbage();
        Vegetables lettuce = new Lettuce();
        //水果
        Fruit apple = new Apple();
        Fruit orange = new Orange();

        //小明的小日子越來越好了
        XiaoMing<Vegetables> xiaoMing = new XiaoMing();
        XiaoMei<Fruit> xiaoMei = new XiaoMei();
        //小明喜欢吃蔬菜,这里小明泛型指定的类型是Vegetables,也就是说小明setFood方法只能传入Vegetables或者Vegetables子类Cabbage,Lettuce
        xiaoMing.setFood(cabbage);
        //小美喜欢吃水果,同上小美只能吃水果
        xiaoMei.setFood(apple);

        xiaoMing.eatFood(xiaoMing.getFood());
        xiaoMei.eatFood(xiaoMei.getFood());
    }
}
3.2、extends 多个的情况
/**
 * 泛型类
 * @param <T> 指定的类型必须继承自Vegetables 并且实现VegetableJuice接口
 *            注:这里还可以implements很多接口,但是类最多extends一个,且有多个接口的情况下 extends 的类必须在最前面
 */
public class XiaoMing <T extends Vegetables & VegetableJuice> extends Person  {

    private T food;

    public XiaoMing() {
        name = "小明";
    }

    public T getFood() {
        return food;
    }

    public void setFood(T food) {
        this.food = food;
    }

    public  void eatFood(T t){
        System.out.println(name+"开始享用"+t);
        System.out.println();
    }
}
public class Food {

    protected String name;
    protected String price;

    public static void main(String[] args) {
       /**
         * extends多个的情况
         */
        //蔬菜
        Vegetables cabbage = new Cabbage();
        Vegetables lettuce = new Lettuce();
        //水果
        Fruit apple = new Apple();
        Fruit orange = new Orange();

        //只指定Vegetables类直接报错
//        XiaoMing<Vegetables> xiaoMing = new XiaoMing();
        //指定的类型必须继承自Vegetables 并且实现VegetableJuice接口
        XiaoMing<LettuceJuice> xiaoMing = new XiaoMing();
        //
        xiaoMing.setFood(new LettuceJuice());
        xiaoMing.eatFood(xiaoMing.getFood());
   }
}
/**
 * 蔬菜汁
 */
public class LettuceJuice extends Vegetables implements VegetableJuice{

}
3.2、super关键字,泛型下界
public class Food {

    protected String name;
    protected String price;

    public static void main(String[] args) {
        /**
         *  ? super关键字,泛型下界
         */
        //蔬菜 extends food类
        Vegetables cabbage = new Cabbage();
        Vegetables lettuce = new Lettuce();

        //水果 extends food类
        Fruit apple = new Apple();
        Fruit orange = new Orange();

        //肉类 extends food类
        Meat pork = new Pork();
        Meat beef = new Beef();

        //水果篮子只能放水果,不能放其他类型食品
        List<? super Fruit> fruitPlate = new ArrayList<Fruit>();

        //放蔬菜,肉类直接报错
        fruitPlate.add(cabbage);
        fruitPlate.add(pork);
        //可以放水果
        fruitPlate.add(apple);
        fruitPlate.add(orange);
   }
}

4、JVM如何实现的泛型?

4.1、擦除

JVM 版本兼容性问题,JDK1.5 以前,为了确保泛型的兼容性,JVM 对泛型进行了擦除,所以说Java 中的泛型,它只在程序源代码中存在,编译后在字节码中 已经替换为原来的原生类型,并且在相应的地方进行了强制类型转换,所以泛型实际上是Java 的语法糖,基于这种方法实现的泛型称为伪泛型。
我们可以通过"Javap -v Food.class"命令将Food.class文件进行反汇编,得到如下图。

QQ截图20201219222030.png QQ截图20201219223015.png
4.2、弱记忆

上面说到Java中的泛型在编译的过程中进行了擦除,为什么我们通过反射还能获取到泛型具体的类型?因为在编译的过程中编译器对泛型信息做了保留。

QQ截图20201219223226.png

Signature 是其中最重要的 一项属性,它的作用就是存储一个方法在字节码层面的特征签名,这个属性中保存的参数类型并不是原生类型,而是包括了参数化类型的信息,这就是为什么泛型擦除后,我们可以通过反射获取到泛型参数化类型的依据。

上一篇 下一篇

猜你喜欢

热点阅读