泛型之最-构建类型安全的Java代码神器,精解RxJava的泛型玩法!


作为Java中最富有争议的语言特性之一,泛型自从在JDK 1.5中被引入以来,就被热烈讨论和广泛应用。很多开发者为之称赞,认为它让代码更安全、更可读。但另一些开发者则觉得泛型语法晦涩难懂,不太受待见。那么泛型到底是造物还是弊端?让我们一探究竟!


一、泛型的作用与定义

泛型的本质是参数化类型,可以在定义类/接口或方法时预留类型位置,在使用时再指定具体的数据类型。

不使用泛型的写法:

List list = new ArrayList();
list.add("hello"); 
list.add(123); // 编译器不会报错,但是存在类型安全隐患

使用泛型的写法:

List<String> list = new ArrayList<>();
list.add("hello"); // 正确
list.add(123); // 编译器会报错,防止了ClassCastException

1、泛型的作用

  • 类型安全:泛型的主要目的是提高类型安全。在没有泛型之前,集合类只能存储 Object 类型的对象,这就需要在取出对象时强制类型转换,这增加了错误的可能性。泛型避免了这种类型转换。
  • 消除类型擦除:在 Java 泛型出现之前,集合类使用 Object 类型作为元素类型,这被称为类型擦除。泛型通过在编译时检查类型来消除类型擦除。
  • 代码复用:泛型允许开发者编写出可重用的代码。一个泛型类或者接口可以与多种类型一起工作,而不需要为每种类型编写新的类或接口。
  • 提高代码的可读性:泛型使得代码更加清晰,因为类型信息在编译时就已经确定,这使得阅读和理解代码更加容易。

2、泛型的定义

泛型可以通过以下几种方式定义:

  • 类和接口:在类名或接口名后使用尖括号 <> 来定义类型参数。
public class Box<T> {
    private T t;
    public void set(T t) { this.t = t; }
    public T get() { return t; }
}
  • 方法:在方法的返回类型前使用尖括号 <> 来定义泛型。
public <E> List<E> getList(E element) {
    List<E> list = new ArrayList<E>();
    list.add(element);
    return list;
}
  • 通配符:使用 ? 来定义通配符,允许不确定的类型。
public void addAll(List<?> list) {
    // 可以添加任何类型的对象到 list 中
}

二、通配符与嵌套

有时会遇到需要使用不确定泛型类型的情况,比如方法的参数或返回值。这时可以使用?通配符来代替具体的类型。

List<?> list = new ArrayList<String>(); // 合法,? 是任何类型的实例
List<Object> objs = new ArrayList<?>(); // 不合法

还可以使用有限制的通配符,如<? extends Foo>表示Foo或其子类的实例。

泛型类型也可以嵌套使用,比如Map<String, List<Integer>>


三、泛型上下边界


Java 泛型的上下界限定了泛型参数的取值范围,这是为了在提供类型安全性的同时,还能保持一定的灵活性。泛型的上下界限通过使用通配符 ? 来实现,并可以配合使用 extendssuper 关键字。


1、上界限通配符(Extends)

使用 ? extends T 表示通配符的上界限,意味着通配符代表的类型是 T 或 T 的任何子类。这种用法常用于只能读取数据的场景,因为编译器无法确保可以安全地插入 T 的任何超类类型的实例。


2、下界限通配符(Super)

使用 ? super T 表示通配符的下界限,意味着通配符代表的类型是 T 或 T 的任何超类。这种用法常用于只能添加数据的场景,因为编译器无法确保可以安全地读取 T 的任何子类类型的实例。


3、代码案例

下面是一个使用泛型上界限和下界限的案例:

public class GenericUpperLowerBoundExample {
    // 使用上界限通配符的泛型方法,只能读取数据
    public static <T> void printListWithUpperBound(List<? extends T> list) {
        for (T item : list) {
            System.out.println(item);
        }
    }

    // 使用下界限通配符的泛型方法,只能添加数据
    public static <T> void addToListWithLowerBound(List<? super T> list, T item) {
        list.add(item);
    }

    public static void main(String[] args) {
        // 创建一个 Integer 类型的列表
        List<Integer> intList = new ArrayList<>();
        intList.add(1);
        intList.add(2);

        // 创建一个 Number 类型的列表
        List<Number> numberList = new ArrayList<>();
        numberList.add(1.1);
        numberList.add(2.2);

        // 使用上界限通配符的泛型方法
        // 可以传递 Number 或其子类类型的列表
        printListWithUpperBound(intList); // 正确,因为 Integer 是 Number 的子类
        printListWithUpperBound(numberList); // 正确

        // 使用下界限通配符的泛型方法
        // 可以向 Number 或其超类类型的列表中添加元素
        Number numberItem = 3.3;
        addToListWithLowerBound(numberList, numberItem); // 正确
        addToListWithLowerBound(intList, numberItem); // 错误,因为 Number 不是 Integer 的超类

        // 打印修改后的列表
        System.out.println("Modified numberList contains: " + numberList);
    }
}

在这个例子中:

  • printListWithUpperBound 方法使用上界限通配符 ? extends T,它只能用于读取操作,因为列表中的元素是 T 或 T 的子类,所以可以安全地读取。
  • addToListWithLowerBound 方法使用下界限通配符 ? super T,它只能用于添加操作,因为列表可以接受 T 或 T 的超类,所以可以安全地添加 T 类型的实例。

四、RxJava中的泛型应用

RxJava广泛应用了泛型编程,其中Observable和Function类都使用了嵌套的泛型类型。

Observable.just("hello")
    .map(new Function<String, Integer>() {
        @Override
        public Integer apply(String s) {
            return s.length();
        }
    })
    .subscribe(new Consumer<Integer>() {
        @Override
        public void accept(Integer len) {
            System.out.println(len);
        }
    });

这个例子中,Observable的泛型参数是String,map操作符的Function的泛型是<String, Integer>(输入String,输出Integer),subscribe的Consumer是<Integer>。通过泛型的使用,数据流的类型被明确定义,提高了代码的安全性和可读性。


五、泛型的注意事项

  • 静态方法/字段

静态方法或字段不能引用泛型类型参数,因为它们在加载时就需要确定类型。可以使用通配符代替。

  • 泛型擦除

泛型只存在于编译期,在运行时会被擦除掉。所有的泛型参数都会被替换为Object类型,为了保证类型安全会执行必要的类型转换。了解泛型擦除后的真实字节码,有助于理解泛型背后的工作机制。

  • 桥接方法

为了实现泛型的多态,编译器会生成桥接方法(Bridge Method)执行必要的类型擦除和转换。桥接方法可能会对性能造成一些影响,需要注意。

  • 类型推断

从Java 7开始,可以让编译器自动推断泛型的类型参数,这被称为"钻石操作符"。比如List<String> list = new ArrayList<>()可以简写为List<String> list = new ArrayList<>()

以上就是本文的全部内容,希望对你有所启发。欢迎在评论区分享你在使用Java泛型中的心得和体会,让我们共同进步!


  • 29
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

w风雨无阻w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值