Java 找不到带有双冒号运算符的正确重载方法
Posted
技术标签:
【中文标题】Java 找不到带有双冒号运算符的正确重载方法【英文标题】:Java cannot find correct overloaded method with double colon operator 【发布时间】:2018-01-01 21:37:04 【问题描述】:当使用双冒号运算符来引用重载方法时,Java 似乎无法确定要使用的正确方法。考虑这个例子:
public class A
private void setter(final Number value)
private void setter(final Optional<Number> value)
private void setter2(final Optional<Number> value)
private <T> void useSetter(final Consumer<Optional<T>> a)
private void callMethod()
useSetter(this::setter); // Error here
useSetter(this::setter2);
第一次调用useSetter
无法编译并给出以下错误:
Cannot infer type argument(s) for <T> useSetter(Consumer<Optional<T>>)
The type A does not define setter(Optional<Object>) that is applicable here
但是,第二个调用编译得很好,这意味着问题出在setter
的重载上。只有一个 setter
重载适用,所以我不明白为什么这不起作用。
可以通过使用指定参数类型的 lambda 来解决此问题,但这会更加冗长。
useSetter((final Optional<Number> v) -> setter(v));
有没有更好的方法来处理这种情况,还是我被困在这个奇怪的怪癖中?
【问题讨论】:
您使用最新版本的 java8 吗?有许多针对 lambdas 的错误修正,即使是在较晚的版本中(内部版本号 >100)。 如果您注释掉出现错误的行,它会编译吗? 记录在案:A)它在 Eclipse 中也出现错误 B)您的示例不需要 getter。您可以删除所有相关内容......您仍然会收到错误消息。始终努力寻找“最小”的例子! jep,因为我认为这是 java 类型擦除搞砸的另一种情况...... 这是设计使然。有关详细信息,请参阅 this answer 或 this answer。显式类型化 lambda 表达式的替代方法是为方法调用提供显式类型参数,因此您也可以通过this.<Number>useSetter(this::setter);
解决此问题。
【参考方案1】:
编译您的private <T> void useSetter(final Consumer<Optional<T>> a)
方法的捕获是Optional<Object>
。编译器试图告诉你它不能强制类型匹配任何已知的捕获。
Main.java:12: error: incompatible types: cannot infer type-variable(s) T
useSetter(this::setter); // Error here
^
(argument mismatch; invalid method reference
no suitable method found for setter(Optional<Object>)
method A.setter(Number) is not applicable
(argument mismatch; Optional<Object> cannot be converted to Number)
method A.setter(Optional<Number>) is not applicable
(argument mismatch; Optional<Object> cannot be converted to Optional<Number>))
where T is a type-variable:
T extends Object declared in method <T>useSetter(Consumer<Optional<T>>)
一种解决方案是使用private <T> void setter(final Optional<? super T> value)
为泛型参数化可选类型创建绑定。另一种选择是向编译器private void setter(final Optional<? super Number> value)
暗示一些强制能力。
class A<T>
private void setter(final Number value)
private <T> void setter(final Optional<? super T> value)
private void setter2(final Optional<Number> value)
private <T> void useSetter(final Consumer<Optional<T>> a)
private void callMethod()
useSetter(this::setter); // no more error
useSetter(this::setter2);
public static void main(String [] args)
class B
private void setter(final Number value)
private void setter(final Optional<? super Number> value)
private void setter2(final Optional<Number> value)
private <T> void useSetter(final Consumer<Optional<T>> a)
private void callMethod()
useSetter(this::setter); // no more error
useSetter(this::setter2);
public static void main(String [] args)
您可以查看ideone here。
这两个选项都不是完美的,因为它会通过允许传递 Optional<Object>
为您的代码引入一些可替代性,但是如果您避免直接使用原始类型,您应该没问题。
【讨论】:
这修复了编译器错误,但在实际代码中,setter 会将值存储在Optional<Number>
字段中。这将需要 setter 方法进行可能在运行时引发错误的强制转换。看起来这实际上允许使用任何可选值(即setter(Optional.of(new ArrayList<>()));
)调用setter
,这很容易导致错误。
是的,这正是我在答案末尾提出的观点。您可以按照我建议的方式打开编译器以这样做,但它允许Optional<Object>
捕获。这意味着任何扩展 Object 的东西的可选,意味着任何东西。问题询问如何绕过我展示的 java 的强类型要求,但是它需要开发人员检查方法实现中的适当类型。
您更详细的解决方案在问题中起作用的原因是它为编译器提供了Optional<Number>
的具体类型捕获,正如您在我的回答中的 IDEOne 输出中看到的那样,与方法匹配无需任何强制、提示或技巧。以上是关于Java 找不到带有双冒号运算符的正确重载方法的主要内容,如果未能解决你的问题,请参考以下文章