回避 javac "ambiguous" 对仅参数 lambda 是 void 还是 non-void 不同的重载方法的警告
Sidestep the javac "ambiguous" warning for overloaded methods that only differ by whether parameter lambda is void or non-void
我正在使用以下方法(以及其他方法)编写实用程序 class:
class FunctionNamingUtils {
public static <T> Consumer<T> named(String name, Consumer<T> delegate) {
class NamedConsumer implements Consumer<T> {
@Override public void accept(T t) { delegate.accept(t); }
@Override public String toString() { return name; }
}
return new NamedConsumer();
}
public static <T, R> Function<T, R> named(String name, Function<T, R> delegate) {
class NamedFunction implements Function<T, R> {
@Override public R apply(T t) { return delegate.apply(t); }
@Override public String toString() { return name; }
}
return new NamedFunction();
}
}
编译器抱怨:
Warning: java:
named(java.lang.String,java.util.function.Consumer) in ... is
potentially ambiguous with
named(java.lang.String,java.util.function.Function) in ...
我确实明白警告的目的 - 根据 lambda returns 是一个值还是 void 我们将采用一种方式或另一种方式,并且对于很难看到的单语句 lambda。
问题是,在这种情况下,这正是我们想要的,我想使用重载来减少必须记住相同功能的两个方法名称的认知负担(我已经不得不这样做与 namedPredicate
妥协,这与布尔函数冲突)。
我正在寻找任何想法 - 关于抑制警告或以不同方式表达 API。重点是客户端的清晰度和易用性。
我只关心Java8+。
其实我对 lambda 的理解是不完整的。事实证明,任何返回值的 lambda 都可以合法地用于 void lambda 的位置。
引用自errorprone/FunctionalInterfaceClash
JLS 15.12.2.1 says that lambdas whose body is a statement expression
are compatible with functional interfaces whose function type is
void-returning or value returning:
A lambda expression (§15.27) is potentially compatible with a
functional interface type (§9.8) if all of the following are true:
The arity of the target type’s function type is the same as the arity
of the lambda expression. If the target type’s function type has a
void return, then the lambda body is either a statement expression
- (§14.8) or a void-compatible block (§15.27.2). If the target type’s
function type has a (non-void) return type, then the lambda body is
either an expression or a value-compatible block (§15.27.2).
换句话说,考虑到上面的实现,传递一个消费者和一个功能性的 lambda 会很好地解决,但是有一个用例,我们想要使用实际上 returns一个值。
例如这两个会失败:
Functions.named(name, Objects::requireNonNull);
Functions.named(name, it -> Objects.requireNonNull(it));
为了让它工作,我们需要像这样添加显式语句块:
Functions.named(name, it -> { Objects.requireNonNull(it); });
虽然这是一个边缘案例,我可以在 API 中记录它,但这改变了可读性权衡,所以现在我将方法重命名为:
Functions.fun(name, delegate)
Functions.con(name, delegate)
Functions.pre(name, delegate)
Functions.sup(name, delegate)
虽然没有 named
好,但它仍然可读且一致。
我简要考虑了 namedFunction
样式名称,但考虑到这些名称在代码库中的使用频率,我宁愿让它们更简洁。
我正在使用以下方法(以及其他方法)编写实用程序 class:
class FunctionNamingUtils {
public static <T> Consumer<T> named(String name, Consumer<T> delegate) {
class NamedConsumer implements Consumer<T> {
@Override public void accept(T t) { delegate.accept(t); }
@Override public String toString() { return name; }
}
return new NamedConsumer();
}
public static <T, R> Function<T, R> named(String name, Function<T, R> delegate) {
class NamedFunction implements Function<T, R> {
@Override public R apply(T t) { return delegate.apply(t); }
@Override public String toString() { return name; }
}
return new NamedFunction();
}
}
编译器抱怨:
Warning: java: named(java.lang.String,java.util.function.Consumer) in ... is potentially ambiguous with named(java.lang.String,java.util.function.Function) in ...
我确实明白警告的目的 - 根据 lambda returns 是一个值还是 void 我们将采用一种方式或另一种方式,并且对于很难看到的单语句 lambda。
问题是,在这种情况下,这正是我们想要的,我想使用重载来减少必须记住相同功能的两个方法名称的认知负担(我已经不得不这样做与 namedPredicate
妥协,这与布尔函数冲突)。
我正在寻找任何想法 - 关于抑制警告或以不同方式表达 API。重点是客户端的清晰度和易用性。
我只关心Java8+。
其实我对 lambda 的理解是不完整的。事实证明,任何返回值的 lambda 都可以合法地用于 void lambda 的位置。
引用自errorprone/FunctionalInterfaceClash
JLS 15.12.2.1 says that lambdas whose body is a statement expression are compatible with functional interfaces whose function type is void-returning or value returning:
A lambda expression (§15.27) is potentially compatible with a functional interface type (§9.8) if all of the following are true:
The arity of the target type’s function type is the same as the arity of the lambda expression. If the target type’s function type has a void return, then the lambda body is either a statement expression
- (§14.8) or a void-compatible block (§15.27.2). If the target type’s function type has a (non-void) return type, then the lambda body is either an expression or a value-compatible block (§15.27.2).
换句话说,考虑到上面的实现,传递一个消费者和一个功能性的 lambda 会很好地解决,但是有一个用例,我们想要使用实际上 returns一个值。
例如这两个会失败:
Functions.named(name, Objects::requireNonNull);
Functions.named(name, it -> Objects.requireNonNull(it));
为了让它工作,我们需要像这样添加显式语句块:
Functions.named(name, it -> { Objects.requireNonNull(it); });
虽然这是一个边缘案例,我可以在 API 中记录它,但这改变了可读性权衡,所以现在我将方法重命名为:
Functions.fun(name, delegate)
Functions.con(name, delegate)
Functions.pre(name, delegate)
Functions.sup(name, delegate)
虽然没有 named
好,但它仍然可读且一致。
我简要考虑了 namedFunction
样式名称,但考虑到这些名称在代码库中的使用频率,我宁愿让它们更简洁。