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

总结

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

相关推荐
怒放吧德德9 小时前
Netty 4.2 入门指南:从概念到第一个程序
java·后端·netty
雨中飘荡的记忆11 小时前
大流量下库存扣减的数据库瓶颈:Redis分片缓存解决方案
java·redis·后端
心之语歌13 小时前
基于注解+拦截器的API动态路由实现方案
java·后端
华仔啊14 小时前
Stream 代码越写越难看?JDFrame 让 Java 逻辑回归优雅
java·后端
ray_liang14 小时前
用六边形架构与整洁架构对比是伪命题?
java·架构
Ray Liang16 小时前
用六边形架构与整洁架构对比是伪命题?
java·python·c#·架构设计
Java水解16 小时前
Java 中间件:Dubbo 服务降级(Mock 机制)
java·后端
SimonKing20 小时前
OpenCode AI辅助编程,不一样的编程思路,不写一行代码
java·后端·程序员
FastBean20 小时前
Jackson View Extension Spring Boot Starter
java·后端
Seven9721 小时前
剑指offer-79、最⻓不含重复字符的⼦字符串
java