栏目分类:
子分类:
返回
终身学习网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
终身学习网 > IT > 软件开发 > 后端开发 > Java

Java泛型的PECS原则

Java 更新时间:发布时间: 百科书网 趣学号

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

一个自定义的栈

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

使用E[]保存元素

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

import java.util.Arrays;
import java.util.EmptyStackException;

public class Stack {
    private static final int DEFAULT_INITIAL_CAPACITY = 16;
    private E[] elements;
    private int size;

    
    @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;


public class StackV2 {
    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 Extends,Consumer Super的缩写,具体的含义我们在下面进行介绍。

Producer Extends

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

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

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

为了解决这个问题,我们需要做出如下修改,也就是把参数类型改成Iterable,这是一种有限制的通配符类型(bounded wildcard type),意思是"E的某个子类型的Iterable接口"。pushAll的source参数产生E实例供Stack使用,也就是source是生产者,因此source的类型是Iterable

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

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

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

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

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

总结

PECS原则也就是说,如果参数化类型表示一个生产者E,就使用,如果参数化类型表示一个消费者E,则使用

转载请注明:文章转载自 www.051e.com
本文地址:http://www.051e.com/it/957084.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 ©2023-2025 051e.com

ICP备案号:京ICP备12030808号