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

First of all, please take me. Most of the time I work in Scala (and only sometimes on the JVM side) or other languages, so my Java (8) knowledge is a bit limited!

The code I have for refactoring is filled with null checks. I wanted to make setting / unsetting some of the pojo attributes a little nicer and enjoyed being able to use Java 8 to work.

So, I created this:

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

      

And I used it like this:

setOnlyIfNotNull(newUserName, user::setName);

      

Junit4 test looks like this:

@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");
}

      

And it works. I was pleased with myself until I asked a colleague to revise the code. After explaining what I did, he clarified that he thought it would not work as functions are still not first class citizens in Java and User-pojo does not extend the functional interface. Also interfaces are provided at the class level, not at the function level.

Now I am wondering why the test is working and am I using something wrong here? Naively, it just seemed to me that the Java compiler knew the setter signature was T setT(T value)

the same as for Consumer<T>

.

edit: To develop a little more: if I change the test to fail, eg. with assertThat(user.getUserName()).isEqualTo("Something");

it fails with Failure comparison as expected!

+3


source to share


1 answer


Invalid view by several counts:

  • FunctionalInterface

    the interface is not required to be functional. This is just a compiler hint to flag an error when something marked with this annotation does not meet the criteria (being an interface with only one abstract method)

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

    • A type is an interface type, not an annotation type, enumeration, or class.
    • An annotated type satisfies the requirements of a functional interface.

    However, the compiler will handle any interface that conforms to the definition of a functional interface as a functional interface, regardless of whether the FunctionalInterface annotation is present in the interface declaration.

  • In any case, it is not User

    that is supposed to be functional, it is an interface Consumer

    that is functional.
  • While functions may indeed not be first-class values ​​(although see here ), user::setFoo

    it is not a "raw" function, it is one that creates an object that implements Consumer

    (in this case) and invokes user.setFoo()

    whatever argument is passed. The mechanism is superficially similar to how anonymous inner classes declare a class and immediately instantiate it. (But there are significant differences in the mechanism behind it.)


But the strongest argument is that your code clearly works using only the official and documented Java API. So saying "but Java doesn't support it" is kind of a weird idea.

+7


source







All Articles