如何返回具有多种类型的对象

Posted

技术标签:

【中文标题】如何返回具有多种类型的对象【英文标题】:How to return an object with multiple types 【发布时间】:2015-04-20 23:01:53 【问题描述】:

让我们举个例子让它更容易。我构建了一个列表,构造函数采用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&lt;Integer&gt;RandomAccess 的实现的情况下调用构造函数,例如 ArrayList&lt;Integer&gt;。所以有这个列表的人:List&lt;Integer&gt; list = new ArrayList&lt;&gt;(); 不能做new MyList(3, list);(因为它是用List&lt;Integer&gt; 而不是ArrayList&lt;Integer&gt; 声明的)。

我的另一个解决方案是:

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,我讨厌这样做。

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

【问题讨论】:

尝试使用 | (或运算符)而不是 & (和运算符)。我不确定这是否可行,但值得一试。 所以如果你想将开发者限制在RandomAccess“确保他不会破坏表演”,那么你为什么还要让他们调用你的带有List 的构造函数,显然没有实现RandomAccess?这只是为了美观吗? 我需要打电话给List.get() 【参考方案1】:

您可以采用Collections.unmodifiableList 使用的解决方案。有一个静态方法,而不是一个公共构造函数,它返回两个实现之一,一个实现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 是私有的。

【讨论】:

【参考方案2】:

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

鼓励通用列表算法检查给定列表是否 在应用算法之前是这个接口的一个实例 如果将其应用于顺序,将提供较差的性能 访问列表,并在必要时更改其行为以保证 可接受的性能。

您的构造函数可能有两个实现。如果实现了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);
    

【讨论】:

您的回答和 pbabcdefp 对我来说已经足够好了。如果我需要,我会存储一些数据。所以我想阻止开发人员使用实现 RandomAccess 的列表。有没有一种程序化的方式来做到这一点?还是我唯一的选择是 javadoc?【参考方案3】:

如果你的类需要一个随机访问列表,那么你的类应该处理这个问题,而不是把你的类的需求推给调用者。此外,在构造函数中做乘法会更简单——你必须在某个时候做,但尽早做意味着你可以扔掉很多代码:

class MyList extends ArrayList<Integer> 

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

这就是你所需要的,并且它更安全:通过你的 immemtation,你的列表和调用者都有一个对列表的引用。如果在调用构造函数后调用更改了列表,则使用相乘列表的其他代码会意外地看到列表中的值发生变化。

【讨论】:

你认为“你必须在某个时候做”的假设很可能是错误的。完全有可能这是一个包含许多项目的列表,其中很少有人真正访问过(因此需要随机访问)。 @sprinter 我刚刚做了一个性能测试:在输入列表大小为 10 个随机数的情况下运行此代码所需的时间不到 5 微秒(在平均 PC 上,预热后)来创建新的相乘的数字 - 每个元素 0.0000005 秒。我不了解你,但我可以忍受“性能打击”来清除所有代码。 我的意思是数以百万计。坦率地说,如果列表中有 10 个项目,那么数据结构并不重要。 此外,您建议我存储列表的元素,我正试图避免这种情况。而且我不能假设O(n) 构造函数。该解决方案可以通过静态工厂方法实现。

以上是关于如何返回具有多种类型的对象的主要内容,如果未能解决你的问题,请参考以下文章

基于另一个值的具有多种对象类型的可编码解码属性

TypeScript 具有相同参数的多种返回类型

具有多种类型的函数声明

如何在返回多种类型的方法中注入返回类型?

键入具有多种键值类型的 Dict [重复]

如何编写返回多种数据类型值的Java函数?