使用泛型在Java中存储公共超类型
假设我有一个方法“mix”,它获取两个可能不同类型的列表T和S,并返回一个包含这两个元素的列表。为了类型安全,我想指定返回的列表是R类型,其中R是T和S共同的超类型。例如:使用泛型在Java中存储公共超类型,java,generics,bounded-wildcard,Java,Generics,Bounded Wildcard,假设我有一个方法“mix”,它获取两个可能不同类型的列表T和S,并返回一个包含这两个元素的列表。为了类型安全,我想指定返回的列表是R类型,其中R是T和S共同的超类型。例如: List<Number> foo = mix( Arrays.asList<Integer>(1, 2, 3), Arrays.asList<Double>(1.0, 2.0, 3.0) ); 将阴影显示在List2的实例上,这样做不好 <R, T extends
List<Number> foo = mix(
Arrays.asList<Integer>(1, 2, 3),
Arrays.asList<Double>(1.0, 2.0, 3.0)
);
将
阴影显示在List2
的实例上,这样做不好
<R, T extends S&T, S extends R> List<R> mix ...
列表混合。。。
解决阴影问题,但编译器不接受
<R super T, S extends R> List<R> mix ...
列表混合。。。
被编译器拒绝,因为下界通配符不能存储在命名变量中(仅用于?super X
表达式)
我可以将参数移动到类本身,如List2
,但是类型信息实际上不应该放在实例级别,因为它只用于一个方法调用,并且每次需要在不同参数上调用方法时都必须重新转换对象
据我所知,泛型无法做到这一点。我所能做的最好的事情就是返回一个原始的
List2
,并在调用站点进行转换,就像引入泛型之前一样。有谁有更好的解决方案吗?如问题和评论中所述,以下签名将是理想的选择:
<R super T, S extends R> List<R> mix(List<S> otherList)
作为一种解决方法,强制转换一个
可选总是安全的。在您的问题中,我唯一不确定的是您是否已经知道这些子类扩展了哪个超类型,或者您想要一个完全通用的方法,在该方法中,您可以传递任何给定超类的两个子类型
在第一个案例中,我最近做了类似的事情,使用了一个抽象类和几个子类型:
public <V extends Superclass> List<Superclass> mix(List<V> list1, List<V> list2) {
List<Superclass> mixedList;
mixedList.addAll(list1);
mixedList.addAll(list2);
}
公共列表组合(列表1、列表2){
列表混合列表;
mixedList.addAll(列表1);
mixedList.addAll(列表2);
}
后一种情况要复杂得多。我建议您重新考虑您的设计,因为mix方法位于超类或知道超类及其子类型的类中更有意义,因为您返回的是超类列表
如果确实要这样做,则必须将List2重构为List2,并执行以下操作:
public <R, V extends R> List<R> mix(List<V> list1, List<V> list2) {
List<R> mixedList;
mixedList.addAll(list1);
mixedList.addAll(list2);
return mixedList;
}
公共列表组合(列表1、列表2){
列表混合列表;
mixedList.addAll(列表1);
mixedList.addAll(列表2);
返回混合列表;
}
为什么要使用S extends T
而不是S extends R
?另外,如果您可以显示方法的主体-mix
,效果会更好。你到底在里面干什么?您真的需要这些类型参数吗?看起来你可以用有界通配符代替。事实上,我不理解R
type参数的需要。因为您只想返回一个列表
,所以只需将其作为返回类型即可。坦白地说,我看不到泛型方法在这里有任何用途。R是t和S的共同超类。对于双精度和整数,它可以是数字。对于StringBuilder和字符串,它可以是CharSequence。对于扫描器和比较器,它可能是对象。我认为没有办法做到这一点。除非在类型列表2
的声明中声明T
与R
的关系。这将简单地将方法声明中的类型参数移动到类declaration:classlist2
,我假设这不是您想要的。
Optional<Integer> optionalInt = getSomeOptionalInt();
Number value = optionalInt.or(0.5); // error
Optional<Number> optionalInt = (Optional) getSomeOptionalInt();
Number value = optionalInt.or(0.5); // fine
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]
public <V extends Superclass> List<Superclass> mix(List<V> list1, List<V> list2) {
List<Superclass> mixedList;
mixedList.addAll(list1);
mixedList.addAll(list2);
}
public <R, V extends R> List<R> mix(List<V> list1, List<V> list2) {
List<R> mixedList;
mixedList.addAll(list1);
mixedList.addAll(list2);
return mixedList;
}