《Effective Java》解读第29条:优先考虑泛型

第29条:优先考虑泛型

Java泛型是增强代码类型安全性和可读性的核心工具。不使用泛型的代码会产生大量运行时强制转换,既不安全也容易出错。

一般来说,为集合声明参数化、jdk使用泛型方法都不太困难,很少会自己编写一个泛型,但这仍然值得我们花时间去学习。

优点

  • 类型安全:在编译期捕获类型错误,避免ClassCastException。
java 复制代码
// 非泛型 - 编译时无法发现类型错误
List list = new ArrayList();
list.add("string");
Integer i = (Integer) list.get(0); // 运行时ClassCastException

// 泛型 - 编译时发现错误
List<String> list = new ArrayList<>();
list.add("string");
Integer i = list.get(0); // 编译错误
  • 表达力强:代码清楚地声明了它所操作的数据类型。

  • 消除强制转换:使代码更简洁,减少"代码杂音"。

java 复制代码
// 使用泛型前
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0); // 需要强制转换

// 使用泛型后
List<String> list = new ArrayList<>();
list.add("hello");
String s = list.get(0); // 自动转换
  • 促进重用:泛型算法和数据结构可以安全地用于多种类型。

实践

例如编写一个泛型栈,我们一步步来实现。

实现一个简单的栈

java 复制代码
// 优先设计为泛型类
public class Stack<E> {
    private E[] elements;
    private int size = 0;

    public Stack(int initialCapacity) {
        elements = new E[initialCapacity];
    }

    public void push(E e) {
        elements[size++] = e;
    }

    public E pop() {
        E result = elements[--size];
        elements[size] = null; // 消除过时引用
        return result;
    }
}

因为泛型数组是不可具体化的。

两种解决方式:

  1. 方法1:创建Object数组强转为泛型数组
    不过,你要确保非受检的转换是安全的。
  2. 方法2:将elements改为Object数组
    同时在pop时强转为E。

    当然这也是一个非受检的警告。
    都可以通过@SuppressWarnings("unchecked")来消除。

    根据第27条的建议,我们只要在包含未受检转换的任务上禁止警告,而不是在整个pop方法上禁止就可以。

两种解决方式的区别:

  1. 方法1只会转换一次,方法2在每次pop时都会进行转换。
  2. 方法1的可读性会更强,但会造成堆污染,虽然在此情况下并无危害。

堆污染其实就是因为泛型擦除,泛型变量指向错误类型的对象。

有限制类型参数

java 复制代码
// 使用有界类型参数
public static <T extends Comparable<T>> T max(Collection<T> coll) {
    T candidate = null;
    for (T element : coll) {
        if (candidate == null || element.compareTo(candidate) > 0) {
            candidate = element;
        }
    }
    return candidate;
}

可以限制泛型类型,例如只能是Comparable的子类型。

在泛型参数声明中,extends用于同时表示"继承自类"和"实现接口"。

注意点

看过第28条建议的会疑惑这不是相矛盾了,第28条鼓励优先使用列表而非数组。实际上不可能总是或者总想在泛型中使用列表。Java并不是生来就支持列表,因此有些泛型如 ArrayList,必须在数组上实现。为了提升性能,其他泛型如HashMap也在数组上实现。

总结

总而言之,使用泛型比使用需要在客户端代码中进行转换的类型来得更加安全,也更加容易。在设计新类型的时候,要确保它们不需要这种转换就可以使用。这通常意味着要把类做成是泛型的。只要时间允许,就把现有的类型都泛型化。这对于这些类型的新用户来说会变得更加轻松,又不会破坏现有的客户端。

相关推荐
Scott9999HH5 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔5 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社6 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海6 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖7 小时前
JDK 26 新特性详解
java·开发语言
马优晨8 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区9 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大10 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai10 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说10 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json