Java8のアップデートについて

今更な情報ですが、Java8でアップデートされた機能についてまとめました。

ちょっと記憶が曖昧で理解できておらず…とても恥ずかしい思いをした出来事があったので今後**「二度と、そして絶対に忘れるまい」**と改めてここに書いてみた次第です。

まずは全体感と概要を掴む目的で…ソースコードも至ってシンプルな内容となっています。
それぞれのアップデートについて今後別記事にて深堀りしていければと思っています。

参考にして頂ければ幸いです。


##Java7からJava8でアップデートされた主な機能
* インターフェースのstaticメソッド
* インターフェースのデフォルトメソッド
* finalの省略（実質的final）
* ラムダ式（() -> {}）
* メソッド参照
* コンストラクター参照
* レシーバーパラメーター（メソッドの引数this）
* 日付時刻API
* StreamAPI

他


##インターフェースのstaticメソッド
インターフェース上にstaticメソッドを定義できるようになった。
下記例のようにMainクラスにインターフェースを継承してstaticなメソッドを呼ぶことができる。

~~~java:Main.java
public class Main implements A1{
	public static void main(String[] args) {
		System.out.println(A1.getValue());
	}
}

interface A1 {
  public static int getValue() {
      return 1;
  }
}
~~~

####実行結果
~~~bash
1
~~~




##インターフェースのデフォルトメソッド
インターフェースに処理本体が記述できるメソッド（抽象でないメソッド）が定義できるようになった。これを「デフォルトメソッド」と呼ぶ。

~~~java:Main.java
public class Main {
	public static void main(String[] args) {
		A a = new A();
		System.out.println(a.getValue());
	}
}

class A implements B1 {
}

interface B1 {
    default public int getValue() {
        return 3;
    }
}
~~~

####実行結果
~~~bash
3
~~~

##finalの省略（実質的final）
実質的にfinal（事実上のfinal（effectively final））な変数にはfinalを付けなくてもよくなった。
ラムダ式の中で使われる変数が実質的にfinalとなってしまう例
name変数が実質的にfinalとなってしまう（final修飾子をつけてもOK）

~~~java:Main.java
public class Main {
	public static void main(String[] args) {
	    String name = "Java!";
        HelloJava g = a -> System.out.println("Hello " + name);
        name = "World!";   //ここでコンパイルエラー
        g.test(name);
	}
}

interface HelloJava {
    void test(String name);
}
~~~

####実行結果
~~~sh
ラムダ式から参照されるローカル変数は、finalまたは事実上のfinalである必要があります
~~~

##ラムダ式（() -> {}）
関数インターフェース（抽象メソッドが1つだけ定義されているインターフェース）の変数に代入する箇所ではラムダ式を渡すことが出来る。

####匿名クラスを使った例
~~~java:Main.java
public class Main {
	public static void main(String[] args) {
	    B1 b = new B1();
	    b.test("Java");
	}
}

class B1 implements A1 {
    @Override
    public void test(String n){
        System.out.println("Hello " + n);
    }
}

interface A1 {
    void test(String name);
}
~~~

####ラムダ式を使った例
~~~java:Main.java
public class Main {
	public static void main(String[] args) {
	    String name = "Java";
	    A1 a = (n) -> {
	        System.out.println("Hello " + n );
	    };
	    a.test(name);
	}
}

interface A1 {
    void test(String name);
}
~~~

####実行結果
~~~sh
Hello Java
~~~

####メリット
匿名クラス（一度だけしか使わないようなクラス）を用いる場合よりも関数型インターフェース（抽象メソッドを1つだけ持つインターフェース）の記述が簡潔になることにある。 ラムダ式で記述することによって、プログラムが冗長になることを防ぎ、より本質的な部分の記述に集中することができるようになる。

##メソッド参照
関数型インターフェース（抽象メソッドが1つだけ定義されているインターフェース）の変数にメソッドそのものを代入することが出来る。
これを「メソッド参照」と呼ぶ。

~~~java:Main.java
public class Main {
	public static void main(String[] args) {
	    String name = "Java";
	    A1 a = System.out::println;
	    a.test("Hello " + name);
	}
}

interface A1 {
    void test(String name);
}
~~~

####実行結果
~~~sh
Hello Java
~~~


##コンストラクター参照
メソッド参照と同様に、コンストラクターも関数型インターフェース（抽象メソッドが1つだけ定義されているインターフェース）の変数に代入することが出来る。
これを「コンストラクター参照」と呼ぶ。

※java.util.functonパッケージのSupplierインターフェースを使用

~~~java:Main.java
import java.util.function.Supplier;

public class Main {
	public static void main(String[] args) {
	    Supplier<B1> b = B1::new;
	    System.out.println(b.get().getName("Java"));
	}
}

class B1 {
    public String getName(String name) {
		return "Hello " + name;
	}
}
~~~

####実行結果
~~~sh
Hello Java
~~~

##レシーバーパラメーター（メソッドの引数this）
メソッドの先頭の引数にthisが指定できるようになった。
これを明示的なレシーバーパラメーター（explicit receiver parameters）（コンパイル時のエラーメッセージ的には「明示的なthisパラメーター」）と呼ぶ。

~~~java:Main.java
public class Main {
	public static void main(String[] args) {
        B1 b = new B1();
        System.out.println(b.getName());
	}
}

class B1 {
    public String getName(B1 this) {
		return "Hello Java";
	}
}
~~~

####実行結果
~~~sh
Hello Java
~~~


##日付時刻API
java.time.LocalDateクラスが導入された。このクラスの特徴としてはimmutable(不変)であること。※一度フィールドの値が設定されるとそれ以降その値が変更されないオブジェクトである。
メリットとしては次のようなことが挙げられる

* そのオブジェクトの値が変更されたかどうかを確かめる必要がない。
* スレッドセーフである。
* データの複製を考える必要がない。
* 複数クライアントによるデータ共有が可能である。

~~~java:Main.java
import java.time.LocalDate;

public class Main {
	public static void main(String[] args) {
        LocalDate a = LocalDate.of(2021, 4, 1);
        LocalDate b = LocalDate.parse("2021-04-01");
        System.out.println(a);
        System.out.println(b);
	}
}
~~~

####実行結果
~~~sh
2021-04-01
2021-04-01
~~~

##Stream API
コレクション、配列、I/Oリソースなどのデータ提供元となるデータソースを元に、集計操作を行うAPI。
ストリームはある処理結果を次の処理のデータソースとして渡すことができる。そのため、データソースを元に様々な処理を通してデータを加工することができる。

####streamを使わない例
~~~java:Main.java
import java.util.*;

public class Main {
	public static void main(String[] args) {
		
		List<String> list = Arrays.asList("bb", "aa", "cc");
				
		for (int i=0; i<list.size(); i++) {
		    String str = list.get(i).toUpperCase();
		    list.set(i, str);
		}
		
		Collections.sort(list);
		for(String str : list) {
		    System.out.print(str + " ");
		}
		
		System.out.println();
	}
}
~~~

####streamを使った例
~~~java:Main.java
import java.util.*;

public class Main {
	public static void main(String[] args) {
		
		List<String> list = Arrays.asList("bb", "aa", "cc");
		list.stream().sorted().map(s -> s.toUpperCase()).forEach(s -> System.out.print(s + " "));
		
}
~~~

####実行結果
~~~sh
AA BB CC
~~~
　　

---


####参考にさせて頂いたサイト

http://www.ne.jp/asahi/hishidama/home/tech/java/uptodate.html


####実行確認
https://dokojava.jp/sources/Main.java