In the 23rd number of Today Software Magazine we started a discussion about what Java SE8 brings new. Almost unanimously, Java specialists state that the lambda expressions, as a general topic, but also the implications they bring about, represent the most important features of the current version. That is why I thought it useful for the first article to be on this particular topic.
The discussions in this article are carried out at a higher difficulty level, in order to highlight some aspects of performance, productivity and reducing the size of the written code.
For the beginning, I come back to lambda expressions. Through the lambda expressions we can create anonymous methods. However, sometimes, the lambda expressions call methods that already have a name.
In order to clarify things, I would like to go back to the example from the article mentioned in the beginning. We have a Product class with two features, name and price, getters and setters. We will also add to our project a POJO class, where there are different comparing methods, with signatures close to the compare() method of the Comparator interface:
public class ProductComparisons {
public int compareByName(
Product a, Product b) {
return a.getName().
compareTo(b.getName());
}
public int compareByPrice(
Product a, Product b) {
return a.getPrice() -
b.getPrice();
}
}
In the test class we will create two Product objects, p1 and p2, which we will place in an array:
Product[] basket = { p1, p2 };
We will sort the array by using the expressions:
ProductComparisons myComparison =
new ProductComparisons();
Arrays.sort(basket,
(a,b)->myComparison.
compareByName(a, b));
This syntax is possible because the result of the lambda expression is a class marked @FunctionalInterface, in our case, Comparator.
Instead of using lambda expressions, Java SE8 offers the possibility of using method references, through the domain operator. The syntax is thus much simplified.
I go back to our example and I apply the previous operator. The syntax becomes:
Arrays.sort(basket,
myComparison::compareByName);
We have the following types of references:
Arrays.sort(basket,
myComparison::compareByName);
Supplier s = Product::new;
where the Supplier is from
java.util.function.Supplier;
Another topic I would like to bring to your attention is connected to interfaces. The interfaces used to contain, until this version, only signatures of methods and constants. Java 8 proposes an approach to improve interface usability. If we add new signatures to the interface, then the classes implementing it should be re-written. In order to avoid the process of writing again, the default methods were introduced. Besides signatures and constants, the interfaces will thus contain implementations, too.
I will provide a simple example, to highlight the new approaches:
public interface MyInterface {
void myClassicMethod();
default void myDefaultMethod() {
System.out.println("hello default!");
}
static void myStaticMethod(){
System.out.println("hello static!");
}
}
Respectively the class implementing the interface:
public class MyClass implements MyInterface {
@Override
public void myClassicMethod() {
System.out.println("hello classic!");
}
}
As a test, we have:
public static void main(String[] args) {
MyInterface mine = new MyClass();
mine.myClassicMethod();
mine.myDefaultMethod();
MyInterface.myStaticMethod();
}
Thus, we refer to the three behaviours:
The inheritance process also supports this new behaviour. Therefore:
I will not conclude this article without describing some of the APIs used in the previous discussions.
java.util.stream, which provides classes for operations on streams of elements. The streams are different from the collections in the following aspects:
The discussions on Java SE8 will be continued in the future editions of TSM. Until then, have a pleasant reading and a beautiful summer!