今更な情報ですが、Java8でアップデートされた機能についてまとめました。
ちょっと記憶が曖昧で理解できておらず…とても恥ずかしい思いをした出来事があったので今後「二度と、そして絶対に忘れるまい」と改めてここに書いてみた次第です。
まずは全体感と概要を掴む目的で…ソースコードも至ってシンプルな内容となっています。
それぞれのアップデートについて今後別記事にて深堀りしていければと思っています。
参考にして頂ければ幸いです。
Java7からJava8でアップデートされた主な機能
- インターフェースのstaticメソッド
- インターフェースのデフォルトメソッド
- finalの省略(実質的final)
- ラムダ式(() -> {})
- メソッド参照
- コンストラクター参照
- レシーバーパラメーター(メソッドの引数this)
- 日付時刻API
- StreamAPI
他
インターフェースのstaticメソッド
インターフェース上にstaticメソッドを定義できるようになった。
下記例のようにMainクラスにインターフェースを継承してstaticなメソッドを呼ぶことができる。
public class Main implements A1{
public static void main(String[] args) {
System.out.println(A1.getValue());
}
}
interface A1 {
public static int getValue() {
return 1;
}
}
実行結果
1
インターフェースのデフォルトメソッド
インターフェースに処理本体が記述できるメソッド(抽象でないメソッド)が定義できるようになった。これを「デフォルトメソッド」と呼ぶ。
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;
}
}
実行結果
3
finalの省略(実質的final)
実質的にfinal(事実上のfinal(effectively final))な変数にはfinalを付けなくてもよくなった。
ラムダ式の中で使われる変数が実質的にfinalとなってしまう例
name変数が実質的にfinalとなってしまう(final修飾子をつけてもOK)
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);
}
実行結果
ラムダ式から参照されるローカル変数は、finalまたは事実上のfinalである必要があります
ラムダ式(() -> {})
関数インターフェース(抽象メソッドが1つだけ定義されているインターフェース)の変数に代入する箇所ではラムダ式を渡すことが出来る。
匿名クラスを使った例
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);
}
ラムダ式を使った例
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);
}
実行結果
Hello Java
メリット
匿名クラス(一度だけしか使わないようなクラス)を用いる場合よりも関数型インターフェース(抽象メソッドを1つだけ持つインターフェース)の記述が簡潔になることにある。 ラムダ式で記述することによって、プログラムが冗長になることを防ぎ、より本質的な部分の記述に集中することができるようになる。
メソッド参照
関数型インターフェース(抽象メソッドが1つだけ定義されているインターフェース)の変数にメソッドそのものを代入することが出来る。
これを「メソッド参照」と呼ぶ。
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);
}
実行結果
Hello Java
コンストラクター参照
メソッド参照と同様に、コンストラクターも関数型インターフェース(抽象メソッドが1つだけ定義されているインターフェース)の変数に代入することが出来る。
これを「コンストラクター参照」と呼ぶ。
※java.util.functonパッケージのSupplierインターフェースを使用
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;
}
}
実行結果
Hello Java
レシーバーパラメーター(メソッドの引数this)
メソッドの先頭の引数にthisが指定できるようになった。
これを明示的なレシーバーパラメーター(explicit receiver parameters)(コンパイル時のエラーメッセージ的には「明示的なthisパラメーター」)と呼ぶ。
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";
}
}
実行結果
Hello Java
日付時刻API
java.time.LocalDateクラスが導入された。このクラスの特徴としてはimmutable(不変)であること。※一度フィールドの値が設定されるとそれ以降その値が変更されないオブジェクトである。
メリットとしては次のようなことが挙げられる
- そのオブジェクトの値が変更されたかどうかを確かめる必要がない。
- スレッドセーフである。
- データの複製を考える必要がない。
- 複数クライアントによるデータ共有が可能である。
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);
}
}
実行結果
2021-04-01
2021-04-01
Stream API
コレクション、配列、I/Oリソースなどのデータ提供元となるデータソースを元に、集計操作を行うAPI。
ストリームはある処理結果を次の処理のデータソースとして渡すことができる。そのため、データソースを元に様々な処理を通してデータを加工することができる。
streamを使わない例
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を使った例
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 + " "));
}
実行結果
AA BB CC