如何 return 具有多种类型的对象
How to return an object with multiple types
让我们举个例子,让它更容易理解。我构建了一个列表,构造函数采用 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
,我讨厌这样做。
我很确定有更好的方法,但它是什么?
您可以采用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
),但它实际上不起作用。例如,如果 list
是 Collections$UnmodifiableRandomAccessList
的实例,则甚至不可能将其转换为同时实现 List
和 RandomAccess
的类型,因为 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);
}
}
让我们举个例子,让它更容易理解。我构建了一个列表,构造函数采用 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
,我讨厌这样做。
我很确定有更好的方法,但它是什么?
您可以采用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
),但它实际上不起作用。例如,如果 list
是 Collections$UnmodifiableRandomAccessList
的实例,则甚至不可能将其转换为同时实现 List
和 RandomAccess
的类型,因为 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);
}
}