gpt4 book ai didi

java - 在 Java 中使用泛型存储公共(public)父类(super class)型

转载 作者:搜寻专家 更新时间:2023-10-30 20:01:56 25 4
gpt4 key购买 nike

假设我有一个方法“mix”,它接受两个可能不同类型 T 和 S 的列表,并返回一个包含两者元素的列表。为了类型安全,我想指定返回的列表是 R 类型,其中 R 是 T 和 S 共有的父类(super class)型。例如:

List<Number> foo = mix(
Arrays.asList<Integer>(1, 2, 3),
Arrays.asList<Double>(1.0, 2.0, 3.0)
);

要指定这一点,我可以将方法声明为

static <R, T extends R, S extends R> List<R> mix(List<T> ts, List<S> ss)

但是如果我想制作 mix 怎么办?实例方法而不是静态方法,在类 List2<T> 上?

<R, T extends R, S extends R> List<R> mix ...

隐藏<T>List2 的实例上, 所以这不好。

<R, T extends S&T, S extends R> List<R> mix ...

解决了阴影问题,但不被编译器接受

<R super T, S extends R> List<R> mix ...

被编译器拒绝,因为下界通配符不能存储在命名变量中(仅用于 ? super X 表达式)

我可以将参数移至类本身,例如 List2<R, T extends R, S extends R> ,但类型信息在实例级别确实没有任何业务,因为它仅用于一个方法调用,并且每次您想要在不同参数上调用该方法时都必须重新转换对象。

据我所知,没有办法用泛型来做到这一点。我能做的最好的就是返回一个原始的 List2并将其转换到调用点,就像在引入泛型之前一样。有人有更好的解决方案吗?

最佳答案

如问题和评论中所述,以下签名是理想的:

<R super T, S extends R> List<R> mix(List<S> otherList)

当然,R super T is not allowed by the language (请注意,polygenelubricants 在链接帖子上的回答是错误的 - 正如您的问题所示,此语法有一些用例)。

这里没有办法取胜 - 您只有几种解决方法中的一种可供选择:

  • 求助于对原始类型使用签名。不要这样做。
  • 保留 mix静态方法。这实际上是一个不错的选择,除非出于与多态性相关的原因它需要成为类接口(interface)的一部分,或者您计划 mix如此常用的方法,以至于您认为保持静态是 Not Acceptable 。
  • mix 的签名解决过于严格,并记录调用者需要进行某些未经检查的强制转换。这类似于 Guava Optional.or 不得不做。来自该方法的文档:

Note about generics: The signature public T or(T defaultValue) is overly restrictive. However, the ideal signature, public <S super T> S or(S), is not legal Java. As a result, some sensible operations involving subtypes are compile errors:

Optional<Integer> optionalInt = getSomeOptionalInt();
Number value = optionalInt.or(0.5); // error

As a workaround, it is always safe to cast an Optional<? extends T> to Optional<T>. Casting [the above Optional instance] to Optional<Number> (where Number is the desired output type) solves the problem:

Optional<Number> optionalInt = (Optional) getSomeOptionalInt();
Number value = optionalInt.or(0.5); // fine

对你来说不幸的是,并不总是安全地施放List2<? extends T>List2<T> .例如,类型转换 List2<Integer>List2<Number>可以允许 Double添加到本应容纳的东西中 Integer s 并导致意外的运行时错误。异常(exception)情况是 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将变得更清洁并且没有类型安全风险。我会确保有充分的理由不保持这种状态。

关于java - 在 Java 中使用泛型存储公共(public)父类(super class)型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18303724/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com