如何将简单的 setter 解释为 Consumer<T>?

How is a simple setter interpreted as Consumer<T>?

首先,请多多包涵。大部分时间我都在使用 Scala(有时只是在 JVM 端)或其他语言,所以我的 Java (8) 知识有点有限!

我必须重构的代码到处都是空检查。我想让一些 pojo 属性的 setting/overriding 更好一点,并且很高兴能够使用 Java 8 来完成这项工作。

所以我创建了这个:

private <T> void setOnlyIfNotNull(final T newValue, Consumer<T> setter) {
    if(newValue != null) {
        setter.accept(newValue);
    }
}

并像这样使用它:

setOnlyIfNotNull(newUserName, user::setName);

junit4-test 看起来像这样:

@Test
public void userName_isOnlyUpdated_ifProvided() {
   User user = new User("oldUserName");

   UserUpdateRequest request = new UserUpdateRequest().withUserName("newUserName");   
service.updateUser(user, request); // This calls setOnlyIfNotNull behind the curtain. And it does invoke the setter ONLY once!

 assertThat(user.getUserName()).isEqualTo("newUserName");
}

这行得通。在我请同事进行代码审查之前,我对自己很满意。在解释我所做的事情后,他详细说明他认为这行不通,因为函数仍然不是 Java 中的第一个 class 公民,并且 User-pojo 没有扩展 FunctionalInterface。还在 class 级别而非功能级别提供接口。

现在我想知道,为什么测试有效,我在这里滥用了什么吗?天真的我只是简单地想象 Java 编译器知道 setter 的 T setT(T value) 签名与 Consumer<T>.

的相同

编辑:详细说明一下:如果我将测试更改为失败,例如使用 assertThat(user.getUserName()).isEqualTo("Something"); 它失败并出现预期的 comparisonFailure!

评论在几个方面是错误的:

  1. FunctionalInterface 不是接口正常运行所必需的。当标有此注释的内容不符合标准(作为只有一个抽象方法的接口)时,它只是一个编译器提示来标记错误

    If a type is annotated with this annotation type, compilers are required to generate an error message unless:

    • The type is an interface type and not an annotation type, enum, or class.
    • The annotated type satisfies the requirements of a functional interface.

    However, the compiler will treat any interface meeting the definition of a functional interface as a functional interface regardless of whether or not a FunctionalInterface annotation is present on the interface declaration.

  2. 无论如何,User 应该是功能性的,Consumer 界面才是功能性的。
  3. 虽然函数确实可能不是 first-class 值(尽管请参阅 here 了解更多信息),但 user::setFoo 不是 "raw" 函数,它是构造创建一个实现 Consumer 的对象(在本例中),并使用传入的任何参数调用 user.setFoo()。该机制表面上类似于匿名内部 classes 声明一个 class 并立即创建一个实例。 (但其背后的机制存在重大差异。)

但最有力的论据是您的代码显然有效,仅使用 Java 的官方和记录 API。所以说 "but Java doesn't support this" 有点奇怪。