如何返回具有多种类型的对象
Posted
技术标签:
【中文标题】如何返回具有多种类型的对象【英文标题】:How to return an object with multiple types 【发布时间】:2015-04-20 23:01:53 【问题描述】:让我们举个例子让它更容易。我构建了一个列表,构造函数采用integer
和List<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
,我讨厌这样做。
我很确定有更好的方法,但它是什么?
【问题讨论】:
尝试使用 | (或运算符)而不是 & (和运算符)。我不确定这是否可行,但值得一试。 所以如果你想将开发者限制在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
),但它实际上不起作用。例如,如果list
是Collections$UnmodifiableRandomAccessList
的一个实例,则甚至不可能将其转换为同时实现List
和RandomAccess
的类型,因为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)
构造函数。该解决方案可以通过静态工厂方法实现。以上是关于如何返回具有多种类型的对象的主要内容,如果未能解决你的问题,请参考以下文章