Java Lambda expression
Tags: java
Published: 2021-01-09 16:32:59

#Overview
Java Lambda implemented in version 8 is a function that a definition of a method can be described as an expression.

## Before Java8

Programmers must have implemented an interface to pass logic codes.

```java
public class Main {
    interface IGreeter {
        void greet(String name);
    }

    static void delegate(IGreeter iGreeter, String name){
        iGreeter.greet(name);
    }

    public static void main(String[] args) throws Exception {
        // need to put an object implemented the interface 'IGreeter'
        delegate(new IGreeter(){
            public void greet(String name){
                System.out.println("Hello, " + name + "."); // => Hello, Taro.
            }
        }, "Taro");
    }
}
```

## From Java8

That can be written simpler than before, and it increased the readability for programmers.

```java
public class Main {
    interface IGreeter {
        void greet(String name);
    }

    static void delegate(IGreeter iGreeter, String name){
        iGreeter.greet(name);
    }

    public static void main(String[] args) throws Exception {
        // The below callings print 'Hello, Taro.'
        delegate((name) -> {System.out.println("Hello, " + name + ".");}, "Taro"); // (arg) -> {sentences}

        delegate(name -> {System.out.println("Hello, " + name + ".");}, "Taro"); // arg -> {sentences}  #if only having one arg, it can omit ()

        delegate(name -> System.out.println("Hello, " + name + "."), "Taro"); // arg -> expression
    }
}
```

# "This" reference
Between a Lambda method and an anonymous interface, the meanings of "this" reference are different, be careful.

```java
public class Main {
    private final String value = "aaa";
    interface IPrinter {
        void printValue();
    }

    void delegate(IPrinter iPrinter){
        iPrinter.printValue();
    }
    
    void callDelegate(){
        delegate(() -> System.out.println("value => " + this.value)); // value => aaa
    }

    public static void main(String[] args) throws Exception {
        // 1) Lambda expression
        new Main().callDelegate(); 
        
        // 2) Anonymous interface
        new Main().delegate(new IPrinter(){
            private final String value = "bbb";
            public void printValue(){
                System.out.println("value => " + this.value); // value => bbb
            }
        }); 

    }
}
```

#Accessible variables
From Lambda expression, Substantially 'final' variables can be accessed.
Even without 'final' keyword, if the value is not changed, it is accessible.

```java
public class Main {
    interface IPrinter {
        void printValue();
    }

    static void delegate(IPrinter iPrinter){
        iPrinter.printValue();
    }

    public static void main(String[] args) throws Exception {
        int aaa = 100;
        delegate(() -> System.out.println(aaa)); // 100
        
        //aaa = 200;
        //If the above sentence is uncommented, a compile error will occur with the below error message.
        //Main.java:14: error: local variables referenced from a lambda expression must be final or effectively final
    }
}
```

#Stateful Lambda expression
If you would like to have a Lambda expression with state, there are two ways.

## 1) Use instance field
```java
public class Main {
    interface ICounter {
        int nextValue();
    }
    
    private int count; // the default value is zero
    ICounter makeCounter(){
        return () -> count++;
    }

    public static void main(String[] args) throws Exception {
        ICounter iCounter = new Main().makeCounter();
        System.out.println(iCounter.nextValue()); // 0
        System.out.println(iCounter.nextValue()); // 1
        System.out.println(iCounter.nextValue()); // 2
    }
}
```

## 2) Use referred object

```java
public class Main {
    interface ICounter {
        int nextValue();
    }
    
    private int count; // the default value is zero
    ICounter makeCounter(){
        // https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicInteger.html
        AtomicInteger count = new AtomicInteger();
        return () -> count.getAndIncrement();
    }

    public static void main(String[] args) throws Exception {
        ICounter iCounter = new Main().makeCounter();
        System.out.println(iCounter.nextValue()); // 0
        System.out.println(iCounter.nextValue()); // 1
        System.out.println(iCounter.nextValue()); // 2
    }
}
```

#Reference
改訂2版　パーフェクトJava
https://gihyo.jp/book/2014/978-4-7741-6685-8