Java 8 Lambda Expressions

Abstract : This article mainly introduces  the background and usage of Java 8 Lambda expressions, as well as the difference between Lambda expressions and anonymous classes. This article is   compiled and organized by OneAPM engineers.

Java  is a first-class object-oriented language, except for some simple data types, everything in Java is an object, even an array is an object, and the instance created by each class is also an object. A function or method defined in Java cannot be completely independent, nor can it take a method as a parameter or return a method to an instance.

Since Swing, we always pass functions to methods through anonymous classes. Here is the old version of the event listener code:

someObject.addMouseListener(new MouseAdapter() {
        public void mouseClicked(MouseEvent e) {

            //Event listener implementation goes here...

        }
    });

 

In the above example, in order to add custom code to the Mouse listener, we define an anonymous inner class MouseAdapter and create its object, in this way, we pass some functional capabilities to the addMouseListener method.

In short, it is not easy to pass values ​​to ordinary methods or functions as parameters in Java. To this end, Java 8 adds a new language-level feature called  Lambda expressions .

Why does Java need Lambda expressions?

If you ignore features such as annotations and generics, the Java language has not changed much since its inception. Java has always been committed to maintaining its object-first character, and after using functional languages ​​such as JavaScript, it becomes clearer how Java emphasizes its object-oriented nature and how strict data types at the source layer are. In fact, functions are not important to Java. In the Java world, functions cannot exist independently.

Introduction to Java 8 Lambda Expression Technology Sharing Part 1

In functional programming languages, functions are first-class citizens, they can exist independently, you can assign them to a variable, or pass them as parameters to other functions. JavaScript is the most typical functional programming language. Click here and here for a clear understanding of the benefits of JavaScript as a functional language. Functional languages ​​provide a powerful feature - closures, which have many advantages over traditional programming methods. A closure is a callable object that records some information from the scope in which it was created. . The closest concept to closures that Java provides today is lambda expressions, and while there are significant differences between closures and lambda expressions, at least lambda expressions are a good replacement for closures.

Steve Yegge's poignant and humorous blog post depicting how the Java world is strictly noun-centric, if you haven't seen it, read it now, it's witty and aptly explained why Java To introduce lambda expressions.

Lambda expressions add missing functional programming features to Java, allowing us to treat functions as first-class citizens. While not entirely true, we'll soon see how lambdas differ from closures, but are infinitely closer to closures. In a language that supports a class of functions, the type of a lambda expression would be a function. However, in Java, Lambda expressions are objects, and they must be attached to a special kind of object type - a functional interface. We'll cover functional interfaces in detail later.

This well-thought-out article by Mario Fusco explains why Java needs Lambda expressions. He explains why modern programming languages ​​must include features like closures.

Introduction to Lambda Expressions

A lambda expression is an anonymous function (which is not entirely true for Java, but for now), simply put, it is a method with no declarations, ie no access modifiers, no return value declaration, and no name.

You can think of it as a shorthand, writing it where you need to use a method. Using this shorthand instead is especially useful when a method is only used once and the definition is short, so that you don't have to bother writing declarations and methods in the class.

Introduction to Java 8 Lambda Expression Technology Sharing Part 2

Lambda expressions in Java are usually  (argument) -> (body) written using syntax such as:

(arg1, arg2...) -> { body }

(type1 arg1, type2 arg2...) -> { body }

Here are some examples of lambda expressions:

(int a, int b) -> {  return a + b; }

() -> System.out.println("Hello World");

(String s) -> { System.out.println(s); }

() -> 42

() -> { return 3.1415 };

 

Structure of Lambda Expressions

Let us understand the structure of a lambda expression.

  • A lambda expression can have zero or more parameters
  • The type of the parameter can be either explicitly declared or inferred from the context. For example: (int a)the (a)same as the effect
  • All parameters must be enclosed in parentheses and separated by commas. For example: (a, b) or  (int a, int b) or (String a, int b, float c)
  • Empty parentheses indicate that the parameter set is empty. E.g:() -> 42
  • When there is only one parameter and its type can be deduced, the parentheses ( ) can be omitted. E.g:a -> return a*a
  • The body of a lambda expression can contain zero or more statements
  • If the body of the lambda expression has only one statement, the curly braces {} can be omitted. The return type of the anonymous function is the same as the body expression
  • If the body of the lambda expression contains more than one statement, the expression must be enclosed in curly braces {} (forming a code block). The return type of the anonymous function is the same as the return type of the code block, or null if there is no return

What is a functional interface

In Java, an interface of type Marker is an interface that has no method or property declarations. Simply put, the marker interface is an empty interface. Similarly, a functional interface is an interface that contains only one abstract method declaration.

java.lang.Runnable It is a functional interface. Only one method is declared in the Runnable interface  void run(). Similarly, the ActionListener interface is also a functional interface. We use anonymous inner classes to instantiate the objects of the functional interface. With Lambda expressions, this One way can be simplified.

Every lambda expression can be implicitly assigned to a functional interface, for example, we can create a reference to the Runnable interface through a lambda expression.

Runnable r = () -> System.out.println("hello world");

 

When no functional interface is specified, the compiler automatically interprets this conversion:

new Thread(
   () -> System.out.println("hello world")
).start();

 

public Thread(Runnable r) { }So, in the code above, the compiler automatically infers that this lambda expression is assigned to the Runnable interface based on the thread class's constructor signature  .

Here are some lambda expressions and their functional interfaces:

Consumer<Integer>  c = (int x) -> { System.out.println(x) };

BiConsumer<Integer, String> b = (Integer x, String y) -> System.out.println(x + " : " + y);

Predicate<String> p = (String s) -> { s == null };

 

@FunctionalInterface  is a newly added interface in Java 8, which is used to indicate that the interface type declaration is a functional interface defined according to the Java Language Specification. Java 8 also declares some functional interfaces that can be used by lambda expressions. When the interface you annotate is not a valid functional interface, you can use @FunctionalInterface to solve compile-level errors.

The following is a custom functional interface: @FunctionalInterface public interface WorkerInterface {

   public void doSomeWork();

}

 

By definition, a functional interface can only have one abstract method, and if you try to add a second abstract method, a compile-time error will be thrown. E.g:

@FunctionalInterface
public interface WorkerInterface {

    public void doSomeWork();

    public void doSomeMoreWork();

}

 

mistake:

Unexpected @FunctionalInterface annotation 
    @FunctionalInterface ^ WorkerInterface is not a functional interface multiple 
    non-overriding abstract methods found in interface WorkerInterface 1 error

Once the functional interface is defined, we can use it in the API, taking advantage of Lambda expressions. E.g:

 // Define a functional interface 
@FunctionalInterface
 public  interface WorkerInterface {

   public void doSomeWork();

}


public class WorkerInterfaceTest {

public static void execute(WorkerInterface worker) {
    worker.doSomeWork();
}

public static void main(String [] args) {

    //invoke doSomeWork using Annonymous class
    execute(new WorkerInterface() {
        @Override
        public void doSomeWork() {
            System.out.println("Worker invoked using Anonymous class");
        }
    });

    //invoke doSomeWork using Lambda expression 
    execute( () -> System.out.println("Worker invoked using Lambda expression") );
}

}

 

output:

Worker invoked using Anonymous class 
Worker invoked using Lambda expression

In the above example, we created a custom functional interface and used it with Lambda expressions. The execute() method can now take a lambda expression as a parameter.

Lambda expression example

The best way to learn lambda expressions is by example.

Threads can be initialized by:

// Old method: 
new Thread( new Runnable() {
@Override
public void run() {
    System.out.println("Hello from thread");
}
}).start();

// New method: 
new Thread(
() -> System.out.println("Hello from thread")
).start();

 

Event handling can be solved using Java 8's Lambda expressions. In the code below, we will add an ActionListener to a UI component using both old and new methods:

  //Old way:
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
    System.out.println("The button was clicked using old fashion code!");
}
});

//New way:
button.addActionListener( (e) -> {
    System.out.println("The button was clicked. From Lambda expressions !");
});

 

What the following code does is print out all the elements in the given array. Note that there is more than one way to use lambda expressions. In the following example, we first create a lambda expression using the usual arrow syntax, and then use Java 8's new double colon (::) operator to convert a regular method into a lambda expression:

//Old way:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
for(Integer n: list) {
   System.out.println(n);
}

//New way:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
list.forEach(n -> System.out.println(n));


//or we can use :: double colon operator in Java 8
list.forEach(System.out::println);

 

In the following example, we use the Predicate functional interface to create a test and print all elements that pass the test, so you can use lambda expressions to specify some logic and do something based on it:

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

public  class Main {

public static void main(String [] a)  {

    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);

    System.out.println("Print all numbers:");
    evaluate(list, (n)->true);

    System.out.println("Print no numbers:");
    evaluate(list, (n)->false);

    System.out.println("Print even numbers:");
    evaluate(list, (n)-> n%2 == 0 );

    System.out.println("Print odd numbers:");
    evaluate(list, (n)-> n%2 == 1 );

    System.out.println("Print numbers greater than 5:");
    evaluate(list, (n)-> n > 5 );

}

public static void evaluate(List<Integer> list, Predicate<Integer> predicate) {
    for(Integer n: list)  {
        if(predicate.test(n)) {
            System.out.println(n + " ");
        }
    }
}

}   

 

output:

Print all numbers: 1 2 3 4 5 6 7 
Print no numbers: 
Print even numbers: 2 4 6 
Print odd numbers: 1 3 5 7 
Print numbers greater than 5: 6 7

The following example uses a lambda expression to print the square of each element in a value, note that we use the .stream() method to convert a regular array to a stream. Java 8 added some awesome streaming APIs. The java.util.stream.Stream  interface contains many useful methods that can be combined with Lambda expressions to produce magical effects. We pass the lambda expression  x -> x*x to the map() method, which acts on all elements in the stream. After that, we use the forEach method to print all the elements in the data:

//Old way:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
for(Integer n : list) {
    int x = n * n;
    System.out.println(x);
}

//New way:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
list.stream().map((x) -> x*x).forEach(System.out::println);

 

The following example computes the sum of the squares of each element of a given number. Note that a lambda expression can do this with just one statement, which is also a rudimentary example of MapReduce. We use map() to square each element, and reduce() to count all elements into one value:

//Old way:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
int sum = 0;
for(Integer n : list) {
    int x = n * n;
    sum = sum + x;
}
System.out.println(sum);

//New way:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
int sum = list.stream().map(x -> x*x).reduce((x,y) -> x + y).get();
System.out.println(sum);

 

Difference Between Lambda Expression and Anonymous Class

A big difference between using anonymous classes and lambda expressions is the use of keywords. For anonymous classes, keywords  this are interpreted as anonymous classes, and for lambda expressions, keywords  this are interpreted as outer classes written as lambdas.

Another difference between lambda expressions and anonymous classes is the way they are compiled. The Java compiler compiles Lambda expressions and converts them into private functions in the class. It  invokedynamic dynamically binds the method using new directives added in Java 7. About how Java compiles Lambda expressions to bytecode, Tal Weiss wrote A good article .

That's it, folks!

Mark Reinhold, Oracle's chief architect, described lambda expressions as the programming model's biggest improvement -- more powerful than generics. Indeed, lambda expressions give Java  programmers features that other functional programming languages ​​lack. Combined with features such as virtual extension methods, lambda expressions can write some excellent code.

Hope this article gave you   all the knowledge about the new features of Java 8 .

Original address: http://viralpatel.net/blogs/Lambda-expressions-java-tutorial/

OneAPM for Java  can go deep into all Java applications to complete application performance management and monitoring, including visibility of code-level performance problems, rapid identification and traceability of performance bottlenecks, real user experience monitoring , server monitoring and end-to-end application performance management . To read more technical articles, please visit  the OneAPM official blog .

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325084946&siteId=291194637