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

总结

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

相关推荐
一路往蓝-Anbo2 小时前
C语言从句柄到对象 (六) —— 继承与 HAL:父类指针访问子类数据
c语言·开发语言·stm32·嵌入式硬件·物联网
北冥有一鲲2 小时前
A2A协议与LangChain.js实战:构建微型软件工厂
开发语言·javascript·langchain
Chen不旧2 小时前
java基于reentrantlock/condition/queue实现阻塞队列
java·开发语言·signal·reentrantlock·await·condition
laplace01233 小时前
Part 3:模型调用、记忆管理与工具调用流程(LangChain 1.0)笔记(Markdown)
开发语言·人工智能·笔记·python·langchain·prompt
寒水馨3 小时前
com.github.oshi : oshi-core 中文文档(中英对照·API·接口·操作手册·全版本)以6.4.0为例,含Maven依赖、jar包、源码
java·后端
0和1的舞者3 小时前
SpringBoot日志框架全解析
java·学习·springboot·日志·打印·lombok
风送雨3 小时前
八周Python强化计划(七)
开发语言·python
ππ很开心6663 小时前
DAY 32 函数专题2:装饰器
开发语言·python
小毅&Nora3 小时前
【Java线程安全实战】② ConcurrentHashMap 源码深度拆解:如何做到高性能并发?
java·安全·多线程