I was reading this tutorial on Java 8 where the writer showed the code:
interface Formula {
double calculate(int a);
default double sqrt(int a) {
return Math.sqrt(a);
}
}
And then said
Default methods cannot be accessed from within lambda expressions. The following code does not compile:
Formula formula = (a) -> sqrt( a * 100);
But he did not explain why it is not possible. I ran the code, and it gave an error,
incompatible types: Formula is not a functional interface`
So why is it not possible or what is the meaning of the error? The interface fulfills the requirement of a functional interface having one abstract method.
Lambda expressions work in a completely different way from anonymous classes in that
this
represents the same thing that it would in the scope surrounding the expression.For example, this compiles
and it prints something like
In other words
this
is aMain
, rather than the object created by the lambda expression.So you cannot use
sqrt
in your lambda expression because the type of thethis
reference is notFormula
, or a subtype, and it does not have asqrt
method.Formula
is a functional interface though, and the codecompiles and runs for me without any problem.
Although you cannot use a lambda expression for this, you can do it using an anonymous class, like this:
This adds little to the discussion, but I found it interesting anyways.
Another way to see the problem would be to think about it from the standpoint of a self-referencing lambda.
For example:
It would seem that this ought to make sense, since by the time the lambda gets to be executed the
formula
reference must have already being initialized (i.e. there is not way to doformula.apply()
untilformula
has been properly initialized, in whose case, from the body of the lambda, the body ofapply
, it should be possible to reference the same variable).However this does not work either. Interestingly, it used to be possible at the beginning. You can see that Maurice Naftalin had it documented in his Lambda FAQ Web Site. But for some reason the support for this feature was ultimately removed.
Some of the suggestions given in other answers to this question have been already mentioned there in the very discussion in the lambda mailing list.
Default methods can be accessed only with object references, if you want to access default method you'd have an object reference of Functional Interface, in lambda expression method body you won't have so can't access it.
You are getting an error
incompatible types: Formula is not a functional interface
because you have not provided@FunctionalInterface
annotation, if you have provided you'll get 'method undefined' error, compiler will force you to create a method in the class.@FunctionalInterface
must have only one abstract method your Interface has that but it is missing the annotation.But static methods have no such restriction, since we can access it with out object reference like below.
It's more or less a question of scope. From the JLS
In your attempted example
the scope does not contain a declaration for the name
sqrt
.This is also hinted at in the JLS
I think it could have been implemented. They chose not to allow it.
That's not exactly true. Default methods can be used in lambda expressions.
prints
4
as expected and uses a default method inside a lambda expression (.mapToInt(val -> val.getDouble())
)What the author of your article tries to do here
is to define a
Formula
, which works as functional interface, directly via a lambda expression.That works fine, in above example code,
Value value = () -> 5
or withFormula
as interface for exampleBut
fails because it's trying to access the (
this.
)sqrt
method but it can't. Lambdas as per spec inherit their scope from their surroundings, meaning thatthis
inside a lambda refers to the same thing as directly outside of it. And there is nosqrt
method outside.My personal explanation for this: Inside the lambda expression, it's not really clear to what concrete functional interface the lambda is going to be "converted". Compare
The very same lambda expression can be "cast" to multiple types. I think of it as if a lambda doesn't have a type. It's a special typeless function that can be used for any Interface with the right parameters. But that restriction means that you can't use methods of the future type because you can't know it.