使用泛型在Java中存储公共超类型

使用泛型在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

假设我有一个方法“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 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;
}