Android Studio2.4 から使えるようになった Java8を再入門

[Android Studio 2.4 Preview 4 から Java8の一部の機能が正式にサポート](https://developers-jp.googleblog.com/2017/05/java-8-language-features-support-update.html)されたので、この機会にJava8の追加機能について学び直そうと思いまとめました。  
Java8についてほぼ触れたことがなかったので、間違っている箇所などありましたら指摘して頂けると嬉しいです。

## Android Studio で Java8を使うために

[こちら](https://developer.android.com/studio/preview/features/java8-support.html)のドキュメントを参考にしています。

**１. Android Studio をアップデート**
 [Android Studio 2.4 Preview 4](https://developer.android.com/studio/preview/index.html) 以上をダウンロードしてください

**２. Androidプラグインをアップデート**
プロジェクト配下のbuild.gradleを開いてAndroidプラグインのバージョンを```2.4.0-alpha6```以上にします。

```groovy
buildscript {
    dependencies {
        classpath 'com.android.tools.build:gradle:2.4.0-alpha6'
    }
}
```

**３.  Javaバージョンを追加**
モジュール配下のbuild.gradleに下記を追加してください。

```groovy
android {
  ...
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
    }
}
```


これでJava8が使えるようになりました。


## [ラムダ式（Lambda expressions）](https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html)

関数型インターフェースに対して匿名クラスの記述を簡略化した書式で実装することができます。  
説明だけでは解りづらいので、まずはコードをみてください。

ラムダ式を使わないJava 8以前の書き方だと以下の様なコードになると思います。

```java
public class Test {

    public static void main(String args[]) {
        Man man = new Man() {
            @Override
            public void laugh(String voice) {
                System.out.println(voice);
            }
        };
        man.laugh("Hahaha!");
    }

    interface Man {
        void laugh(String voice);
    }
}
```

これをJava 8から導入されたラムダ式を使用して書き換えるとこのようになります。

```java
public class Test {

    public static void main(String args[]) {
        Man man = (voice) -> {System.out.println(voice);}
        man.laugh("Hahaha!");
    }

    @FunctionalInterface
    interface Man {
        void laugh(String voice);
    }
}
```

インターフェースの実装部分がかなり簡略化されたのがわかりますね。  
ラムダ式は関数型インターフェース、簡単に説明すると１つの抽象メソッドしか持たないインターフェースの場合に使用することができます。  
ラムダ式の書式は以下のようになります。  
```( 実装するメソッドの引数 ) -> { 処理 }```

 ```実装するメソッドの引数``` は```voice```としましたが、任意の文字列（```x```など）でも問題ありません。   
 引数の前に型の宣言がない事が不思議に思うかもしれませんが、抽象メソッドの定義時に決まっているので（型推論により）型が不要になります。
 引数が単一の場合は```()```も不要になります。 
処理は１行の場合には```{}```は不要です。
以上を踏まえると下記のようにさらに簡略化できます。

```java
Man man = voice -> System.out.println(voice);
```
  
 また、```Man``` インターフェースの前に```@FunctionalInterface```アノテーションが付いているに気がついたでしょうか？  
このアノテーションもラムダ式と一緒に導入され、関数型インターフェースであることを表しています。
もし、```@FunctionalInterface```が定義されているインターフェースにもう一つ抽象メソッドを定義するとコンパイルエラーになります。
もし、いままでラムダ式で記述していたインターフェースにもう一つ抽象メソッドを追加したとします。
その場合、ラムダ式の記述ができなくなってしまうので、新たに匿名クラスでコードを書きなおす必要がでてきます。
このようなことが起きない様に```@FunctionalInterface```をつける事によって実装者に事前に知らせる役目があります。

匿名クラスをラムダ式で書き換えられることからわかる様に、ラムダ式は匿名クラスの仕様を引き継いでいます。
匿名クラスでは```final```で宣言されていないローカル変数にアクセスできませんでしたが、ラムダ式の場合も同様にアクセスすることができません。  
***しかし！***  
Java8からはローカル変数宣言時に値が導入され、それ以降変更されていない場合は```final```とみなし、```final```を付けなくても問題ありません。

```java
public static void main(String args[]) {
    String name = "hoge";
    Man man = new Man() {
        @Override
        public void laugh(String voice) {
            System.out.print(voice);
            System.out.print(name); // コンパイルエラーにならない
        }
    };
    // name = "bar";  // コメントを外すとコンパイルエラーになる。
}
```

匿名クラスとラムダ式では、```this```の参照先に違いがあります。
匿名クラス内で```this```を使用した場合は匿名クラス自身を参照します。
しかし、ラムダ式内で```this```を使用した場合は実装されているクラスを参照します。

```java
Man man = new Man() {
    @Override
    public void laugh(String voice) {
        System.out.println(this.getClass());    // class xxx.yyy.zzz.Test$1
    }
};

Man man2 = voice -> System.out.println(this.getClass());    // class xxx.yyy.zzz.Test
```

## [メソッド参照（Method References）](https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html)

メソッド参照によって、ラムダ式をさらに簡略化した記述ができます。 
この様なインターフェースを使用する場合、

```java 
interface Man {
    void laugh(String voice);
}
``` 

ラムダ式ではこの様に記述していましたが、

```java
Man man = voice -> System.out.print(voice);
```

メソッド参照ではこの様になります。

```java
Man man = System.out::print;
```

引数が消えて、メソッドチェーンに```::```が含まれているのがわかります。 
メソッド参照の書式は下記のようになります。
 ``` クラス名::メソッド名 ```
  
インスタンスメソッドの場合は下記のようになります。

```java
Test test = new Test();
Man man = test::method;
```

メソッド参照は抽象メソッドの引数と処理に渡す引数が同じ場合、要するに抽象メソッドと処理するメソッドのシグネチャが同じ場合に使用することができます。  
```System$out#print(String s)```と```Man#laugh(String voice)```はどちらも```String```型の引数を```１つ```であることからメソッド参照が可能なわけです。


引数を２つにして見ましょう。

```java
interface Man {
    void laugh(String voice, String voice2);
}
```

``` TextUtils#split(String text, String expression)``` は2つの```String```型を引数に持つため、このようにメソッド参照で記述することができます。

```java
Man man = TextUtils::split;
```

メソッド参照を使うことでコードがスッキリしますが、可読性が落ちるのでラムダ式にとどめておくのがいいでしょう。


## [タイプアノテーション](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/annotation/ElementType.html)

以前からタイプアノテーションはありますが、Java8から新たに```TYPE_PARAMETER```と```TYPE_USE```が追加されました。
***```TYPE_PARAMETER```と```TYPE_USE```は、API level 24 以上で使用可能です。***

タイプアノテーションはアノテーションが定義できる箇所を宣言するために使用します。  
例えば、```@Override```の場合は以下の様に定義されています。  

```java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
```

タイプアノテーションは```@Target(ElementType.METHOD)```のことで、```ElementType.METHOD```はメソッドにこのアノテーションが宣言可能であることを表しています。  
Java8から新しく追加された```TYPE_PARAMETER```と```TYPE_USE```について説明します。

 ``` TYPE_PARAMETER ```はジェネリクスのパラメータに使用することができます。

```java
@Target(ElementType.TYPE_PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestParam {}
```

```java
public class Test<@TestParam E>{

    public <@TestParam F> F method(){
        //...
    }
}
```

``` TYPE_USE ```は型が宣言されている全ての箇所で使用することができます。

```java 
@Target(ElementType.TYPE_USE)
public @interface TestType {}
```
すべての例を書き出しませんが、とりあえず型の宣言がある箇所すべてで使用可能です。  

```java
@TestType    // 型宣言
public class MyApp<@TestType T>    // 型パラメータ宣言
        extends @TestType Application{    // 拡張（or 実装）する型

    @Override
    public void start(@TestType Stage stage) {    // パラメータの型
    }
```




## [インターフェースにDefault と staticメソッドの追加](https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Method.html#isDefault--)

***API level 24以上で使用可能です。
しかし、現段階ではInstant Runに対応していない様です。（[Issue #302460](https://issuetracker.google.com/issues/37150411)）***

### static メソッド

java8からインターフェースにstaticメソッドを定義できるようになりました。

```java
interface Man {
    static void staticMethod(){
        System.out.print("static method");
    }
}
```


staticメソッドを呼び出す場合は普段staticメソッドを呼び出す時と同様です。

```java
Man.staticMethod();
```

### Default

これまではインターフェースは抽象メソッドのみしか定義できませんでしたが、実装メソッドも定義できるようになりました。  
実装メソッドを定義するには、```default```をメソッドの先頭に付けます。

```java
interface Man {
    void laugh();

    default void sayGreeting() {
        System.out.println("Hello");
    }
}
```
上記のインターフェースを実装する場合は、最低限```laugh()```メソッドのみ実装するだけで良くなります。

```java
public class Test {

    public static void main(String args[]) {
        Man man = new Man() {
            @Override
            public void laugh() {
                System.out.println("Hahaha!");
            }
        };
        man.laugh();        // "Hahaha!"
        man.sayGreeting();  // "Hello"
    }

    interface Man {
        void laugh();

        default void sayGreeting() {
            System.out.println("Hello");
        }
    }
}
```
もちろん、```sayGreeting()```をオーバーライドすることで```sayGreeting()```の処理も変更することができます。  
感覚としては、```abstract```クラスににてますね。  
Javaでは多重継承が認められていませんが、インターフェースに限っては多重継承する事ができます。  
なので、今までインターフェースを多重継承した```abstract```クラスを```extends```していたところを```implements```だけで済むようになります。  
また、コールバックインターフェースを実装する際に使用しないメソッドもあると思います。いままでは、それらのメソッドを全てオーバーライドしなくてはなりませんでしたが、
```default```を使用する事によって無駄なメソッドをオーバーライドせずに済むようになりました。


## おわりに
メソッド参照は初見だと全く意味がわかりませんでした。
わかっても慣れていないので、頭混乱します。
できれば使って欲しく無いと思いました。


以上。