Yes, provided that the recursive call uses a name defined in the enclosing environment of the lambda. This means that recursive definitions can only be made in the context of variable assignment and, in fact—given the assignment-before-use rule for local variables—only of instance or static variable assignment. So, in the following example, factorial
must be declared as an instance or static variable.
Example
UnaryOperator<Integer> factorial = i -> i == 0 ? 1 : i * factorial.apply( i - 1 );
Earlier versions of the specification allowed local variables to be used as well. There
is some discussion about the reversal of this decision in the comments on this page.
do these calls consume the stack as classic Java recursion does?
Yes. A new section of this FAQ, coming soon, will explore implementation questions.
The example can be improved a bit to show the static variable assignment and remove optional return keyword and curly brackets.
Yes, it’s worth saying that if the lambda body is an expression then braces and
return
keyword are unnecessary. It’s arguable whether that makes it easier to understand, though. (But I’ve adopted your suggestions.)> given the assignment-before-use rule for local variables
Uh? So this code is illegal if factorial is a local variable?
That makes little sense, because factorial will *not* be used before assignment, since function bodies are not evaluated before the function is invoked in any functional language which deserves this name. I fear however that Java does not deserve this name: What will happen in Java is that factorial will be *captured* before assignment, and while proper functional languages save just a reference to the value, Java must copy the value.
Still, to produce the right value you just need to have a function value with a pointer to itself – that’s easy to do by mutating a field.
Anyway, the “assignment-before-use” rule for local variables, alone, does not explain why that snippet is illegal – you also need an appropriate definition of what is considered “use”, which includes capture.
According to Chapter 16 of the Java Language Specification
So the JLS supports the interpretation of “use” that is implied in this post. Whether those are the best semantics for a functional language is a different question. But then, Java in its initial design had no pretentions to being a functional language.
[Edit, 28-July-2013] This question continues to gives rise to a lot of confusion, both because Paulo’s comment is very reasonable and because a long-superseded version of SoL did state (in §6, with examples) that the assignment-before-use rule would be refined to allow lambdas to use local variables for self-reference. That decision was reversed and the facility removed from the JSR spec in October 2012, because it was felt that there was too much work involved to be worth supporting a corner-case with a simple workaround.