好的,我正在使用的库中有一个 get 或 create 方法,如下所示:
A getOrCreateA( Function< U,A > ) {}
B 延伸 A
但在我的代码中,我接受了
Function<U,B>
但我无法将其输入到上面的方法中。
有人可以解释为什么吗,我可以明白为什么这不适用于列表,但肯定任何创建 B 的东西,本质上都会创建 A
或者任何人都可以提出替代解决方案。
我的用例本质上是特定类型的 getOrCreate:
public B getOrCreate_b(U ignore, Function<U,B> constructor) {
A return_val = ignore.getOrCreate_a(constructor); //<- error
if (return_val instanceof B)
return (B)return_val;
else
return constructor.apply(ignore);
}
(是的,我知道如果函数返回 A,这实际上不会存储 B,它在其他地方处理)
Java 不知道
Function
的第二个参数本质上只是“生产”。该函数生成 B,但从不读取它们。 Function 实现中没有方法将 B 作为参数;它仅显示在返回类型中。
但是,那就是今天了。也许明天有人会补充说(没有人会这样做;java是向后兼容的,但是,lang规范不要求这一点,并且
Function
本身无法声明其第二类型参数不可能出现在消费中)角色)。
因此,java 将通常的规则应用于泛型,即它们是不变的。如果需要
List<Number>
,则只需 List<Number>
即可;尽管 Integer 扩展了 Number,但 List<Integer>
和 List<Object>
都不起作用。毕竟:
List<Integer> ints = new ArrayList<Integer>();
List<Number> nums = ints;
Double d = 5.5;
nums.add(d);
int x = ints.get(0);
尝试理解该代码,您就会明白为什么泛型是不变的,而基本类型(不在
<>
内)是协变的(协变 = 子类型与其超类型之一一样好)。
修复是
getOrCreateA
功能已损坏。它应该被定义如下:
public <U, A> A getOrCreate(U v, Function<? super U,? extends A> constructor) {
注意
? super
和 ? extends
语法:第一个参数是逆变的,第二个参数是协变的:只消耗第一个,只产生第二个。这是可行的,因为这正是函数实际“工作”的方式。如果这是签名,那么您的代码就可以工作。这个技巧(Function<? super I, ? extends O>
)已经结束了,例如java本身的集合API,因为没有理由不这样做。
如果您无法更改底层功能,那么您就无法在不陷入丑陋的转换并收到大量警告的情况下解决此问题。当您使用编写糟糕、损坏的库时,这种情况往往会发生。你不应该使用这样的库或告诉它的作者修复他们的东西。