So what is a lambda expression? It is a block of code that can be assigned to a variable or passed as an argument to a method or passed as an argument to another lambda expression just like any other data. The code can also be invoked whenever required. The main motivation behind supporting this in java is to remove a lot of boiler plate code in using certain API that require some code from the API user, but end up using inner classes just because of java's syntax requirements. The most common such API is the java threading API where we need to be able to tell the API which code to execute in a new thread, but end up implementing Runnable.
The specification is still under development and is continuously changing. This article just gives an idea as to what to expect.
Functional interfaces: The java specification developers hardly ever want to modify the JVM specification, and this case is no exception. So they are making the specification in a way so that lambda can be implemented without any modification in the JVM. So you can compile a class easily with source version 1.8 and target version 1.5.
So the lambda code will be kept in an implementation of an anonymous class implementing an interface that has only one method. Well not exactly, the interface can have more than one method, but it must be implementable by a class that defines only one method. We will call such an interface a functional interface. The following are some examples of functional interfaces.
//A simple case, only one method
//This is a functional interface
public interface Test1{
public void doSomething(int x);
}
//Defines two methods, but toString() is already there in
//any object by virtue of being subclass of java.lang.Object
//This is a functional interface
public interface Test2{
public void doSomething(int x);
public String toString();
}
//The method in Test3 is override compatible with
//the method in Test1, so the interface is still
//functional
public interface Test3 extends Test1{
public void doSomething(int x);
}
//Not functional, the implementation must
//explicitly implement two methods.
public interface Test4 extends Test1{
public void doSomething(long x);
}
//This is a functional interface
public interface Test1{
public void doSomething(int x);
}
//Defines two methods, but toString() is already there in
//any object by virtue of being subclass of java.lang.Object
//This is a functional interface
public interface Test2{
public void doSomething(int x);
public String toString();
}
//The method in Test3 is override compatible with
//the method in Test1, so the interface is still
//functional
public interface Test3 extends Test1{
public void doSomething(int x);
}
//Not functional, the implementation must
//explicitly implement two methods.
public interface Test4 extends Test1{
public void doSomething(long x);
}
Lambda expressions: In java 8, the lambda expressions are just a different syntax to implement functional interfaces using anonymous classes. The syntax is indeed much simpler than that for creating anonymous classes. The syntax mainly is of this form:
argumentList -> body
The argumentList is just like java method argument list - comma separated and enclosed in parentheses, with one exception - if there is only one argument, the parentheses are optional. Also it is optional to mention the types of the arguments. In case the types are not specified, they are inferred. The body can be of two types - expression body and code block body. An expression body is just a valid java expression that returns a value. A code block body contains a code block just like a method body. The code block body has the same syntax as the method body including the mandatory pair of braces.
The following example shows how a new thread is implemented using lambda syntax.
//The thread will keep printing "Hello"
new Thread(() -> { while(true){ System.out.println("Hello"); }}).start();
new Thread(() -> { while(true){ System.out.println("Hello"); }}).start();
The expression syntax is shown in the following example
public interface RandomLongs{
public long randomLong();
}
RandomLongs randomLongs = () -> ((long)(Math.random()*Long.MAX_VALUE));
System.out.println(randomLongs.randomLong());
public long randomLong();
}
RandomLongs randomLongs = () -> ((long)(Math.random()*Long.MAX_VALUE));
System.out.println(randomLongs.randomLong());
Generics and lambda: But what if we want to implement a generic method using lambda? The specification developers have come up with a nice syntax, the type parameters are declared before the type arguments. The following shows an example -
public interface NCopies{
public <T extends Cloneable> List<T> getCopies(T seed, int num);
}
//Inferred types for arguments also supported for generic methods
NCopies nCopies = <T extends Cloneable> (seed, num) -> {
List<T> list = new ArrayList<>();
for(int i=0; i<num; i++)
list.add(seed.clone());
return list;
};
public <T extends Cloneable> List<T> getCopies(T seed, int num);
}
//Inferred types for arguments also supported for generic methods
NCopies nCopies = <T extends Cloneable> (seed, num) -> {
List<T> list = new ArrayList<>();
for(int i=0; i<num; i++)
list.add(seed.clone());
return list;
};
A point to note: The actual interface and method implemented by a lambda expression depends on the context in which it is used. The context can be setup by the existence of either an assignment operation or by the passing of parameter in a method invocation. Without a context, the lambda is meaningless, so its not correct to simply call a method directly on a lambda expression. For example, the following will give a compilation error -
public interface NCopies{
public <T extends Cloneable> List<T> getCopies(T seed, int num);
}
//This code will give a compilation error,
//As the lambda is meaningless without a context
(<T extends Cloneable> (seed, num) -> {
List<T> list = new ArrayList<>();
for(int i=0; i<num; i++)
list.add(seed.clone());
return list;
}).getCopies(new CloneableClass(), 5);
public <T extends Cloneable> List<T> getCopies(T seed, int num);
}
//This code will give a compilation error,
//As the lambda is meaningless without a context
(<T extends Cloneable> (seed, num) -> {
List<T> list = new ArrayList<>();
for(int i=0; i<num; i++)
list.add(seed.clone());
return list;
}).getCopies(new CloneableClass(), 5);
However, the following would be perfectly alright, because there is an assignment context for the lambda.
NCopies nCopies = <T extends Cloneable> (seed, num) -> {
List<T> list = new ArrayList<>();
for(int i=0; i<num; i++)
list.add(seed.clone());
return list;
};
nCopies.getCopies(new CloneableClass(), 5);
List<T> list = new ArrayList<>();
for(int i=0; i<num; i++)
list.add(seed.clone());
return list;
};
nCopies.getCopies(new CloneableClass(), 5);
The stripped down lambda: Lisp's support for lambda is much more flexible than this. The whole lisp language is based on lambda. However, java has to restrict the syntax to fit into its own syntax. Besides, lisp is an interpreted language, which has the advantage of doing stuff in the runtime when all informations are available. Java being a compiled language, it has to stick to much more stringent rules for types and control-flow etc., so as to avoid surprises at runtime. Considering this, the stripped down lambda in java 8 does not look that bad.
6 comments:
Great post, but how can I print it? You don't offer a "print-friendly" view, and if I just tell Firefox to print this view, it clips the right side, truncating many of the lines of text. Thanks.
Thanks its a nice suggestion. I will implement that in some time.
Please don't implement a "print-friendly" feature. If that dude wants a "print-friendly" version, he should just shut up instead.
I can't find lamda usefule. It just seems to remove the parenthesis and class invokation code. Nothing else. It has not reduced the line of code nor does it has modified the functionality.
In other languages if there is something like grep or lamda it has a purpose, I can't see any purpose of implementing it in JAVA8 other than tha Groovy developers will feel comfortable coding in JAVA
I can surely see a lot of influence from scala in this step.
i like it
Post a Comment