Java基础之Java泛型

Java泛型

为什么要使用泛型?

​ Java语言引入泛型的好处是安全简单。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。

定义一个简单泛型类

public class Person <T>{
    private T info;

    public T getInfo() {
        return info;
    }

    public void setInfo(T info) {
        this.info = info;
    }
}

在这里插入图片描述

可以看到好处 编译的时候检查类型安全 继续看下段代码

public static int addInt(int x, int y) {
     return x + y;
}
public static Double addDouble(double x, double y) {
     return x + y;
}
public static <T extends Number> T add(T x, T y) {
    if (x instanceof Integer) {
        return (T) Integer.valueOf(x.intValue() + y.intValue());
    } else if (x instanceof Double) {
        return (T) Double.valueOf(x.doubleValue() + y.doubleValue());
    }
    return (T) Integer.valueOf(-1);
}

看下使用

System.out.println(Utils.addInt(1,2));
System.out.println(Utils.addDouble(0.12,2));
System.out.println(Utils.add(0.13,2));
System.out.println(Utils.add(3,2));

结果输出

3
2.12
2.13
5

可以看的出 提高代码的重用率 看一下,下边的继承关系

public class Fruit
public class Orange extends Fruit
public class Apple extends Fruit
public class HongFuShi extends Apple

public static void print(GenericType<Fruit> p){
       System.out.println(p.getData().getColor());
   }

GenericType<Fruit> a = new GenericType<>();
print(a);
GenericType<Orange> b = new GenericType<>();
//print(b);
//这样会编译错误 虽然 Fruit是Orange的父类
//但是这个GenericType<Fruit>与GenericType<Orange> 是没有任何关系的
//因为泛型擦除的存在 

于是提出了一个通配符类型 ?

? extends X 表示类型的上界,类型参数是 X 的子类

? super X 表示类型的下界,类型参数是 X 的超类

这两种 方式从名字上来看,特别是 super,很有迷惑性,下面我们来仔细辨析这两种方法。

? extends X

? extends X 表示传递给方法的参数,必须是 X 的子类(包括 X 本身)

public static void print2(GenericType<? extends Fruit> p){
    System.out.println(p.getData().getColor());
}

GenericType<Fruit> a = new GenericType<>();
print2(a);
GenericType<Orange> b = new GenericType<>();
print2(b);
//只要是Fruit类和Fruit的子类都可以了

​ 但是对泛型类 GenericType 来说,如果其中提供了 get 和 set 类型参数变量 的方法的话,set 方法是不允许被调用的,会出现编译错误,get 方法则没问题,会返回一个 Fruit 类型的值。

GenericType<? extends Fruit> c =  new GenericType<>();
Apple apple =  new Apple();
Fruit fruit = new Fruit();
//c.setData(apple);
//c.setData(fruit);
Fruit x = c.getData();

​ 原因是 ? extends X 表示类型的上界,所以get的时候返回的一定是x的子类或者是其本身。主要用于安全的访问数据,可以访问x及其子类型,并且不能写入非null的数据。

? super X

表示传递的方法的参数,必须是X超类,包括X本身。

public static void printSuper(GenericType<? super Apple> p){
    System.out.println(p.getData());
}

GenericType<Fruit> fruitGenericType = new GenericType<>();
GenericType<Apple> appleGenericType = new GenericType<>();
GenericType<HongFuShi> hongFuShiGenericType = new GenericType<>();
GenericType<Orange> orangeGenericType = new GenericType<>();
printSuper(fruitGenericType);
printSuper(appleGenericType);
//下边会检查报错 应为HongFuShi是Apple的子类 Orange是Fruit的子类
//printSuper(hongFuShiGenericType);
//printSuper(orangeGenericType);

​ 但是对泛型类 GenericType 来说,如果其中提供了 get 和 set 类型参数变量 的方法的话,set 方法可以被调用的,且能传入的参数只能是 X 或者 X 的子类。

GenericType<? super Apple> x = new GenericType<>();
x.setData(new Apple());
x.setData(new HongFuShi());
//不能set Apple的父类Fruit 这里跟上边的不太一样
//x.setData(new Fruit());
Object data = x.getData();

​ ? super X 表示类型的下界,类型参数是 X 的超类(包括 X 本身),那 么可以肯定的说,get 方法返回的一定是个 X 的超类,那么到底是哪个超类?不 知道,但是可以肯定的说,Object 一定是它的超类,所以 get 方法返回 Object。 编译器是可以确定知道的。对于 set 方法来说,编译器不知道它需要的确切类型, 但是 X 和 X 的子类可以安全的转型为 X。

​ 主要用于安全地写入数据,可以写入 X 及其子类型。

泛型擦除

public void test(List<String> a){}

public void test(List<Integer> a){}
//这段代码会在编译器报错,就能体现出泛型擦除了。

​ 上面这段代码是不能被编译的,因为参数 List<Integer>和 List<String>编 译之后都被擦除了,变成了一样的原生类型 List<E>,擦除动作导致这两种方法的特征签名变得一模一样。

​ 虽然泛型会被擦除,但是在字节码中会有一个属性Signature存储泛型的信息,我们还可以得出结论,擦除法所谓的擦除, 仅仅是对方法的 Code 属性中的字节码进行擦除,实际上元数据中还是保留了泛 型信息,这也是我们能通过反射手段取得参数化类型的根本依据。

总结 泛型的使用的三种方式

泛型类:public class Test}{} T表示未知类型

泛型接口:public interface Test{} 和定义类一样

泛型方法:public void Test(T name){}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值