《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也在数组上实现。

总结

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

相关推荐
ACP广源盛139246256734 小时前
接口适配方案选型|GSV6125@ACP HDMI2.0 转 Type‑C/DP1.4 桥接芯片工程评估
大数据·c语言·开发语言·硬件架构·硬件工程·国产芯片
深入云栈5 小时前
Netty 4.2.x 源码深度解析 (十四):KQueue 传输 —— macOS 高性能 IO 的 Netty 实现
java·后端
信誓旦旦的程序猿5 小时前
【Python 量化取数指南 #13】Python 把行情落库:sqlite 一键存,回测随用随取
java·python·股票数据api·股票数据·股票数据api接口·股票api数据接口·股票量化数据api
小羊没烦恼!5 小时前
【译】利用Asp.net MVC处理文件的上传下载
开发语言·缓存·c#·word·powerpoint·.net
她的男孩5 小时前
头像换了三次还是旧图:秒传 + 预签名 URL + 私有文件权限,四层缓存叠出一个 Bug
java·后端·架构
深入云栈5 小时前
Netty 4.2.x 源码深度解析 (十五):io_uring 传输 —— RingBuffer 与零拷贝 IO
java·后端
MacroZheng5 小时前
装上这款全能增强插件,DeepSeek Harness瞬间高大上了!
java·人工智能·后端
时间的拾荒人5 小时前
Qt 信号与槽机制详解(二):连接方式详解与面试指南
开发语言·qt·面试
long、涯5 小时前
win10安装Maven3.9(图文版)
java·maven
霸道流氓气质5 小时前
LangGraph式图编排引擎Java实现:状态机模型、Checkpoint恢复与生产级落地实战
java·开发语言·数据库