首页 > 编程知识 正文

Java泛型详解,java静态方法使用泛型

时间:2023-05-04 13:51:35 阅读:225862 作者:2730

最近看到第三版《Effective Java》中介绍泛型的PECS原则写的挺好在此记录下来。

一个自定义的栈

假设我们自定义一个带有泛型的栈保存元素时可以使用E[]或者Object[]代码分别如下所示。这里值得我们参考的点是当使用SuppressWarnings(“unchecked”)时要给出注释说明为什么可以忽略掉警告。

使用E[]保存元素

使用E[]保存元素时由于不能创建泛型数组因此需要进行一次强转。

import java.util.Arrays;import java.util.EmptyStackException;public class Stack<E> { private static final int DEFAULT_INITIAL_CAPACITY 16; private E[] elements; private int size; /** * elements数组中只能包含E类型的元素(调用push方法向栈中添加元素)因此这里的强转是安全的。 */ SuppressWarnings(unchecked) public Stack() { this.elements (E[]) new Object[DEFAULT_INITIAL_CAPACITY]; } public void push(E e) { ensureCapacity(); elements[size] e; } public E pop() { if (size 0) { throw new EmptyStackException(); } E result elements[--size]; // 消除引用防止内存泄漏 elements[size] null; return result; } public boolean isEmpty() { return size 0; } private void ensureCapacity() { if (elements.length size) { elements Arrays.copyOf(elements, 2 * size 1); } }} 使用Object[]保存元素

使用Object[]保存元素时每次从数组中获取到元素后需要强转成E类型。

import java.util.Arrays;import java.util.EmptyStackException;/** * author yukaiqiang <yukaiqiangkuaishou.com> * Created on 2022-06-07 */public class StackV2<E> { private static final int DEFAULT_INITIAL_CAPACITY 16; private Object[] elements; private int size; public StackV2() { this.elements new Object[DEFAULT_INITIAL_CAPACITY]; } public void push(E e) { ensureCapacity(); elements[size] e; } public E pop() { if (size 0) { throw new EmptyStackException(); } // push时要求元素类型是E因此这里的强转是安全的 SuppressWarnings(unchecked) E result (E) elements[--size]; // 消除引用防止内存泄漏 elements[size] null; return result; } public boolean isEmpty() { return size 0; } private void ensureCapacity() { if (elements.length size) { elements Arrays.copyOf(elements, 2 * size 1); } }} PECS原则

PECS是Producer ExtendsConsumer Super的缩写具体的含义我们在下面进行介绍。

Producer Extends

现在要扩展Stack的功能增加一个pushAll方法我们第一版的实现可能是这样的。

public void pushAll(Iterable<E> source) { for (E e : source) { push(e); } }

上面的写法在使用时可能会遇到问题如下图所示。 为了解决这个问题我们需要做出如下修改也就是把参数类型改成Iterable<? extends E>这是一种有限制的通配符类型bounded wildcard type意思是E的某个子类型的Iterable接口。pushAll的source参数产生E实例供Stack使用也就是source是生产者因此source的类型是Iterable<? extends E>。

public void pushAll(Iterable<? extends E> source) { for (E e : source) { push(e); } } Consumer Super

现在要扩展Stack的功能增加一个与pushAll对应的popAll方法我们第一版的实现可能是这样的。

public void popAll(Collection<E> destination) { while (!isEmpty()) { destination.add(pop()); } }

上面的写法在使用时可能会遇到问题如下图所示。

为了解决这个问题我们需要做出如下修改也就是把参数类型改成Collection<? super E>意思是E的某种超类集合”。popAll的destination参数通过Stack消费E实例也就是destination是消费者因此destination的类型是Collection<? super E>。

总结

PECS原则也就是说如果参数化类型表示一个生产者E就使用<? extends E>如果参数化类型表示一个消费者E则使用<? super E>。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。