Java 8 lambda expression and first-class values
I would say that Java 8 closures ("Lambdas") are neither mere syntactic sugar nor are they first-class values.
I've addressed the issue of syntactic sugar in an answer to another StackExchange question.
As for whether lambdas are "first class" it really depends on your definition, but I'll make a case that lambdas aren't really first class.
In some sense a lambda wants to be a function, but Java 8 is not adding function types. Instead, a lambda expression is converted into an instance of a functional interface. This has allowed lambdas to be added to Java 8 with only minor changes to Java's type system. After conversion, the result is a reference just like that of any other reference type. In fact, using a Lambda -- for example, in a method that was passed a lambda expression as parameter -- is indistinguishable from calling a method through an interface. A method that receives a parameter of a functional interface type can't tell whether it was passed a lambda expression or an instance of some class that happens to implement that functional interface.
For more information about whether lambdas are objects, see the Lambda FAQ Answer to this question.
Given that lambdas are converted into objects, they inherit (literally) all the characteristics of objects. In particular, objects:
- have various methods like
equals
,getClass
,hashCode
,notify
,toString
, andwait
- have an identity hash code
- can be locked by a
synchronized
block - can be compared using the
==
and!=
andinstanceof
operators
and so forth. In fact, all of these are irrelevant to the intended usage of lambdas. Their behavior is essentially undefined. You can write a program that uses any of these, and you will get some result, but the result may differ from release to release (or even run to run!).
Restating this more concisely, in Java, objects have identity, but values (particularly function values, if they were to exist) should not have any notion of identity. Java 8 does not have function types. Instead, lambda expressions are converted to objects, so they have a lot baggage that's irrelevant to functions, particularly identity. That doesn't seem like "first class" to me.
Update 2013-10-24
I've been thinking further on this topic since having posted my answer several months ago. From a technical standpoint everything I wrote above is correct. The conclusion is probably expressed more precisely as Java 8 lambdas not being pure (as opposed to first-class) values, because they carry a lot of object baggage along. However, just because they're impure doesn't mean they aren't first-class. Consider the Wikipedia definition of first-class function. Briefly, the criteria listed there for considering functions first-class are the abilities to:
- pass functions as arguments to other functions
- return functions from other functions
- assign functions to variables
- store functions in data structures
- have functions be anonymous
Java 8 lambdas meet all of these criteria. So that does make them seem first-class.
The article also mentions function names not having special status, instead a function's name is simply a variable whose type is a function type. Java 8 lambdas do not meet this last criterion. Java 8 doesn't have function types; it has functional interfaces. These are used effectively like function types, but they aren't function types at all. If you have a reference whose type is a functional interface, you have no idea whether it's a lambda, an instance of an anonymous inner class, or an instance of a concrete class that happens to implement that interface.
In summary, Java 8 lambdas are more first-class functions than I had originally thought. They just aren't pure first-class functions.
Do lambda expressions have any use other than saving lines of code?
Lambda expressions do not change the set of problems you can solve with Java in general, but definitely make solving certain problems easier, just for the same reason we’re not programming in assembly language anymore. Removing redundant tasks from the programmer’s work makes life easier and allows to do things you wouldn’t even touch otherwise, just for the amount of code you would have to produce (manually).
But lambda expressions are not just saving lines of code. Lambda expressions allow you to define functions, something for which you could use anonymous inner classes as a workaround before, that’s why you can replace anonymous inner classes in these cases, but not in general.
Most notably, lambda expressions are defined independently to the functional interface they will be converted to, so there are no inherited members they could access, further, they can not access the instance of the type implementing the functional interface. Within a lambda expression, this
and super
have the same meaning as in the surrounding context, see also this answer. Also, you can not create new local variables shadowing local variables of the surrounding context. For the intended task of defining a function, this removes a lot of error sources, but it also implies that for other use cases, there might be anonymous inner classes which can not be converted to a lambda expression, even if implementing a functional interface.
Further, the construct new Type() { … }
guarantees to produce a new distinct instance (as new
always does). Anonymous inner class instances always keep a reference to their outer instance if created in a non-static
context¹. In contrast, lambda expressions only capture a reference to this
when needed, i.e. if they access this
or a non-static
member. And they produce instances of an intentionally unspecified identity, which allows the implementation to decide at runtime whether to reuse existing instances (see also “Does a lambda expression create an object on the heap every time it's executed?”).
These differences apply to your example. Your anonymous inner class construct will always produce a new instance, also it may capture a reference to the outer instance, whereas your (Developer o1, Developer o2) -> o1.getName().compareTo(o2.getName())
is a non-capturing lambda expression that will evaluate to a singleton in typical implementations. Further, it doesn’t produce a .class
file on your hard drive.
Given the differences regarding both, semantic and performance, lambda expressions may change the way programmers will solve certain problems in the future, of course, also due to the new APIs embracing ideas of functional programming utilizing the new language features. See also Java 8 lambda expression and first-class values.
¹ From JDK 1.1 to JDK 17. Starting with JDK 18, inner classes may not retain a reference to the outer instance if it is not used. For compatibility reasons, this requires the inner class not be serializable. This only applies if you (re)compile the inner class under JDK 18 or newer with target JDK 18 or newer. See also JDK-8271717
How do I define a method which takes a lambda as a parameter in Java 8?
Lambdas are purely a call-site construct: the recipient of the lambda does not need to know that a Lambda is involved, instead it accepts an Interface with the appropriate method.
In other words, you define or use a functional interface (i.e. an interface with a single method) that accepts and returns exactly what you want.
Since Java 8 there is a set of commonly-used interface types in java.util.function
.
For this specific use case there's java.util.function.IntBinaryOperator
with a single int applyAsInt(int left, int right)
method, so you could write your method
like this:
static int method(IntBinaryOperator op){
return op.applyAsInt(5, 10);
}
But you can just as well define your own interface and use it like this:
public interface TwoArgIntOperator {
public int op(int a, int b);
}
//elsewhere:
static int method(TwoArgIntOperator operator) {
return operator.op(5, 10);
}
Then call the method with a lambda as parameter:
public static void main(String[] args) {
TwoArgIntOperator addTwoInts = (a, b) -> a + b;
int result = method(addTwoInts);
System.out.println("Result: " + result);
}
Using your own interface has the advantage that you can have names that more clearly indicate the intent.
Java 8 functions and classes
Sort of, but functions to not stand alone as first class citizens like in a functional programming language.
In Java 8 you can add a method to an Interface, or an Enum.
public interface Loggable {
default Logger logger() {
return LoggerFactory.getLogger(this.getClass());
}
}
public enum Level {
HIGH (3),
MEDIUM(2),
LOW (1)
private final int levelCode;
Level(int levelCode) {
this.levelCode = levelCode;
}
public int getLevelCode() {
return this.levelCode;
}
}
There is also a special annotation in Java 8 called the FunctionalInterface that can be used to create a function interface that can be implemented in line without being a member of a Class.
If you really want to do functional programming in Java because you want to avoid maintaining state in lower order functions you can always make those functions static and then treat the Class as a package. This way you don't have to do any object creation.
Related Topics
When Should an Illegalargumentexception Be Thrown
Jtable Getselectedrow Does Not Return the Selected Row Index
Find Duplicate Element in Array in Time O(N)
How to Simulate a Real Mouse Click Using Java
Static Block VS. Initializer Block in Java
File Path Windows Format to Java Format
Use a .Jar Java Library API in C#
How to Implement a Fsm - Finite State MAChine in Java
How Does Java Makes Use of Multiple Cores
How to Modify JSONnode in Java
Java Static Serialization Rules
Copying Text to the Clipboard Using Java
Java Threads and Number of Cores
How to Loop Over a Class Attributes in Java