如何 return 具有多种类型的对象

How to return an object with multiple types

让我们举个例子,让它更容易理解。我构建了一个列表,构造函数采用 integerList<Integer>。我的列表将包含给定列表的所有元素乘以 integer。我的列表不存储新元素,而是即时计算它们:

class MyList extends AbstractList<Integer> implements RandomAccess {
    private final int multiplier;
    private final List<Integer> list;

    public MyList(int multiplier, List<Integer> list) {
        this.multiplier = multiplier;
        this.list = list;
    }

    @Override
    public Integer get(int index) {
        return list.get(index) * multiplier;
    }

    @Override
    public int size() {
        return list.size();
    }
}

然后我们可以用list = [0, 1, 2, 3]调用new MyList(3, list)得到[0, 3, 6, 9].

我想限制开发人员给 MyList 构造函数一个也是 RandomAccess 的列表,以确保他不会破坏性能。

我尝试更改构造函数:

public <E extends List<Integer> & RandomAccess> MyList(int multiplier, E list)

MyList 不是问题,但现在我们不能在不使用 List<Integer>RandomAccess 的实现的情况下调用构造函数,例如 ArrayList<Integer>。因此,拥有此列表的人:List<Integer> list = new ArrayList<>(); 不能执行 new MyList(3, list);(因为它是用 List<Integer> 而不是 ArrayList<Integer> 声明的)。

我的另一个解决方案是这个:

public MyList(int multiplier, List<Integer> list) {
        if(!(list instanceof RandomAccess)) {
            // Do something like log or throw exception
        }
        this.multiplier = multiplier;
        this.list = list;
    }

但现在我无法在编译时检查列表是否实现了 RandomAccess,我需要使用 instanceof,我讨厌这样做。

我很确定有更好的方法,但它是什么?

您可以采用Collections.unmodifiableList使用的解决方案。代替 public 构造函数,有一个静态方法 returns 两个实现之一,一个实现 RandomAccess,另一个不实现。

这是 Collections.unmodifiableList 的代码。

public static <T> List<T> unmodifiableList(List<? extends T> list) {
    return (list instanceof RandomAccess ?
            new UnmodifiableRandomAccessList<>(list) :
            new UnmodifiableList<>(list));
}

我知道你说过你不喜欢使用 instanceof。我也不喜欢,但有时这是最好的做法。

注意使用构造函数的解决方案

public <E extends List<Integer> & RandomAccess> MyList(int multiplier, E list)

不仅丑陋,因为它迫使程序员强制转换(例如转换为 ArrayList),但它实际上不起作用。例如,如果 listCollections$UnmodifiableRandomAccessList 的实例,则甚至不可能将其转换为同时实现 ListRandomAccess 的类型,因为 Collections$UnmodifiableRandomAccessList 是私有的。

如果你的 class需要一个随机访问列表,那么你的class应该处理那个并且不要将您 class 的需求推给来电者。此外,在构造函数中进行乘法会更简单——您必须在某个时候进行,但尽早进行意味着您可以丢弃大量代码:

class MyList extends ArrayList<Integer> {

    public MyList(int multiplier, List<Integer> list) {
        for (Integer i : list)
            add(i * multiplier);
    }
}

这就是您所需要的,并且它更安全:通过您的输入,您的列表和调用者都可以引用该列表。如果在调用构造函数之后调用更改列表,则使用相乘列表的其他代码将意外地看到列表中的值发生更改。

我建议使用 instanceof。事实上,这正是 RandomAccess 文档所建议的:

Generic list algorithms are encouraged to check whether the given list is an instanceof this interface before applying an algorithm that would provide poor performance if it were applied to a sequential access list, and to alter their behavior if necessary to guarantee acceptable performance.

您的构造函数可能有两个实现。如果实现 RandomAccess 则它存储对 List 的引用,否则它创建一个新的 ArrayList 并将所有元素复制到它:

class MyList {
    private final int multiplier;
    private final List<Integer> list;

    public MyList(int multiplier, List<Integer> list) {
        this.multiplier = multiplier;
        if (list instanceof RandomAccess)
            this.list = list;
        else
            this.list = new ArrayList<>(list);
    }

    public int get(int index) {
        return multiplier * list.get(index);
    }
}