如问题和评论中所述,以下签名将是理想的:
<R super T, S extends R> List<R> mix(List<S> otherList)
但当然,R super T
语言不允许(请注意,polygenelubricants 在链接帖子上的回答是错误的 - 正如您的问题所证明的那样,这种语法有一些用例)。
没有办法在这里取胜 - 您只有以下几种解决方法之一可供选择:
- 诉诸于使用原始类型的签名。不要这样做。
- 保持
mix
静态方法。这实际上是一个不错的选择,除非由于与多态性相关的原因它需要成为类接口的一部分,或者您计划mix
成为一种常用的方法,以至于您认为将其保持为静态是不可接受的。
- 接受过度限制的签名
mix
,并记录调用者需要某些未经检查的强制转换。这类似于GuavaOptional.or
必须做的事情。从该方法的文档中:
关于泛型的注意事项:签名public T or(T defaultValue)
过于严格。但是,理想的签名 ,public <S super T> S or(S)
不是合法的 Java。因此,一些涉及子类型的合理操作是编译错误:
Optional<Integer> optionalInt = getSomeOptionalInt();
Number value = optionalInt.or(0.5); // error
Optional<? extends T>
作为一种解决方法,将一个to 转换为始终是安全的Optional<T>
。将 [上述Optional
实例] 转换为Optional<Number>
(Number
所需的输出类型在哪里)可以解决问题:
Optional<Number> optionalInt = (Optional) getSomeOptionalInt();
Number value = optionalInt.or(0.5); // fine
对您来说不幸的是,投射List2<? extends T>
到List2<T>
. 例如,将 a 强制转换List2<Integer>
为 aList2<Number>
可能允许将 aDouble
添加到仅应包含Integer
s 的内容中,并导致意外的运行时错误。例外情况是如果List2
是不可变的(例如Optional
),但这似乎不太可能。
尽管如此,如果您小心并记录类型不安全的代码并附有解释,您仍然可以摆脱这种强制转换。假设mix
有以下签名(和实现,为了好玩):
List<T> mix(final List<? extends T> otherList) {
final int totalElements = (size() + otherList.size());
final List<T> result = new ArrayList<>(totalElements);
Iterator<? extends T> itr1 = iterator();
Iterator<? extends T> itr2 = otherList.iterator();
while (result.size() < totalElements) {
final T next = (itr1.hasNext() ? itr1 : itr2).next();
result.add(next);
final Iterator<? extends T> temp = itr1;
itr1 = itr2;
itr2 = temp;
}
return result;
}
那么您可能有以下呼叫站点:
final List2<Integer> ints = new List2<>(Arrays.asList(1, 2, 3));
final List<Double> doubles = Arrays.asList(1.5, 2.5, 3.5);
final List<Number> mixed;
// type-unsafe code within this scope
{
@SuppressWarnings("unchecked") // okay because intsAsNumbers isn't written to
final List2<Number> intsAsNumbers = (List2<Number>)(List2<?>)ints;
mixed = intsAsNumbers.mix(doubles);
}
System.out.println(mixed); // [1, 1.5, 2, 2.5, 3, 3.5]
同样,对静态的解决将更mix
清洁,并且不会对类型安全造成风险。我会确保有很好的理由不保持这种状态。