Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this Java 8 method reference not working for System.out.println(LocalDate::now);?

Tags:

java

java-8

Maybe I am not fully understanding the concept of Java 8 functional interfaces and/or method references. I just don't understand why the following code has "The target type of this expression must be a functional interface":

import java.time.LocalDate;

interface Today{
    LocalDate getTodayDate(); 
}

Class to test:

class Test{
    public static void main(String[] args) {

        Today today = () -> LocalDate.now();
        System.out.println(today::getTodayDate);//Problem with this one                 
    }   
}
like image 718
MIT Avatar asked May 25 '19 06:05

MIT


People also ask

How do I print a reference method in Java 8?

How to print the values using method reference? in method references you just give the name of the function (println), they don't take arguments. You can create your own function that accepts a string and calls toUpperCase and then println, and then give the name of your function as the method reference name.

What type referencing is allowed in Java 8?

Java provides a new feature called method reference in Java 8. Method reference is used to refer method of functional interface. It is compact and easy form of lambda expression. Each time when you are using lambda expression to just referring a method, you can replace your lambda expression with method reference.

What is system out Println in method reference?

To make the code clearer, you can turn that lambda expression into a method reference: Consumer<String> c = System. out::println; In a method reference, you place the object (or class) that contains the method before the :: operator and the name of the method after it without arguments.

What is the use of BiFunction in Java 8?

Interface BiFunction<T,U,R> This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference. Represents a function that accepts two arguments and produces a result. This is the two-arity specialization of Function .


2 Answers

You're using your method reference in a context where its target type is unknown.

It would have worked if java.io.PrintStream had a method like the following:

public void println(Today dateSupplier){
    print(dateSupplier.getTodayDate());
}

But that's not the signature of println.

There's no reason to do this, but the way to make your code work is to invoke your own interface method, but this can't be justified...

System.out.println(today.getTodayDate()); //calls println(Object)

Unless this contract involves more than one classes/types, you may need to redesign your code.

like image 75
ernest_k Avatar answered Oct 04 '22 14:10

ernest_k


In your question description you say that the error you get is: "The target type of this expression must be a functional interface"; but the line where you show that:

System.out.println(today::getTodayDate);//Problem with this one

would exhibit a different error message. So I guess a broader explanation is needed.

It seems you first need to create a @FunctionInterface called Today for this code to work, something like:

@FunctionalInterface
interface Today {
    LocalDate getTodayDate();
}

When that is created, this would work too:

Today today = () -> LocalDate.now(); 

which would be equivalent of:

Today today = LocalDate::now;

There's not a real need to create Today since java.util.Supplier<T> exists and does the same thing - takes nothing as input and returns T; but if you wanted to play around - you could.

Actually the fact that you have introduced this Today interface will make explaining things a bit easier.

The compiler sees : today::getTodayDate (in the System.out.println(today::getTodayDate); line) and has to "understand" what that is; it could be at least two things : Today or java.util.Supplier - it can't tell for sure; that is why it is said that lambda expressions and methods references are poly expressions - their type is inferred in the context of usage (just like generics). Since the compiler can't tell what that really is, it fails.

You could make an explicit cast to the type for this to compile:

System.out.println((Today) today::getTodayDate);

or:

System.out.println((Supplier<LocalDate>) today::getTodayDate);

But the problem is that even if this compiles and works and prints "something" - what it really prints is un-specified in the JLS and is implementation specific.

like image 36
Eugene Avatar answered Oct 04 '22 14:10

Eugene