新人SEの学習記録

14年度入社SEの学習記録用に始めたブログです。気づけば社会人3年目に突入。

学習記録:Java

[学習記録] Java

参考書籍

Javaプログラマーなら習得しておきたい Java SE 8 実践プログラミング

Javaプログラマーなら習得しておきたい Java SE 8 実践プログラミング

内容:第1章 ラムダ式とは

なぜラムダ式なのか
  • 受け渡すことのできるコードブロック
    • 時間的に経過してから1回以上実行することが可能
  • スレッドのrun()、ソート時に渡されるコンパレータ、ボタンに渡されるイベントハンドラ
    • コードブロックを渡すことでもっと簡単に書ける
ラムダ式の構文
  • パラメータ、矢印、式
    • コードに渡す変数の仕様(型)、矢印、コード
(String first, String second)
    -> Integer.compare(first.length(), secont.length())
  • 式が単一式で収まらないならば、{}で括ってreturn文を持たせる
(String first, String second) -> {
    if (first.length() < second.length())  return -1;
    else if (first.length() > second.length())  return 1;
    else return 0;
}
  • パラメータを持たない場合は空の括弧を書く
() -> {  for (int i = 0; i < 1000; i++)  do();  }
  • パラメータの型が推定可能ならば省略可能
    • Comparatorに代入されているので、String型だと推定可能
Comparator<String> comp =
    (first, second) -> Integer.compare(first.length(), second.length());
  • パラメータが単一なら()も省略できる
  • 結果の型は明示しない
    • 文脈から常に推定される
    • 分岐によっては値を返したり返さなかったりするラムダ式は不正となる
関数型インタフェース
  • 単一の抽象メソッドを持つインタフェースのオブジェクトが期待される場所
    • ラムダ式を提供することができる(=関数型インタフェース)
    • Runnable, Comparator
Arrays.sort(words, (first, second) ->
           Integer.compare(first.length(), second.length()));
  • インタフェースへの変換
    • 構文が短くて単純なものとなる
    • 次の例がその一つ、内部クラスを使うより遥かに読みやすいコードになっている
button.setOnAction(event -> System.out.println("hello!"));
  • 実際、関数型インタフェースへの変換が、Javaラムダ式でできる唯一の行為
    • 他言語では関数式を変数に保存したりできるが、Javaではインタフェースの概念から離れないことにした
  • チェックされる例外の問題
    • ラムダ式の本体がチェックされる例外をスローする場合、
    • 1)インタフェースの抽象メソッドでその宣言が成されている
    • 2)ラムダ式内でその例外をキャッチする
    • のいずれかが必要
メソッド参照
  • 他のコードに渡したい処理と同じ処理を行うメソッドが既に存在する場合
button.setOnAciton(event -> System.out.println(event));

// こう書ける!
button.setOnAction(System.out::println);
コンストラクタ参照

※配列型を使ってコンストラクタ参照が作れる

  • int[]::newはx->new int[x]に相当
    • これを使って欲しい型の配列を生成する指定が可能
// Button[]型が欲しい
Object[] buttons = stream.toArray();

Button[] buttons = strean.toArray(Button[]::new);
変数スコープ
  • ラムダ式内では、エンクロージングスコープの変数の値をキャプチャできる
public static void repeatMessage(String text, int count) {
    Runnable r = () ->
        for (int i = 0; i < count; i++) {
            System.out.println(text);
        }
    };
}
  • ただし、アクセスできるのは実質的finalであるローカル変数のみ
    • 上記の例ではcountやtextの値を変更するとエラーになる
    • 参照型への変更は注意が必要(ArrayList型の変数listにputすることは可能、同じオブジェクトを参照していることは変わらないため)
  • ラムダ式の本体は、ネストしたブロックと同じスコープを持つ
    • 上で定義されたローカル変数と同じ名前を持つパラメータやローカル変数は定義できない
デフォルトメソッド
  • コレクションにループで同じ処理を書く場合
    • forEachメソッドを提供すれば、コードが短くなり理解がしやすくなる
for (int i = 0; i < list.size(); i++)
    System.out.println(list.get(i));

// こっちの方が簡単
list.forEach(System.out::println);
  • 大きな問題
    • CollectionインタフェースにforEachメソッドを追加すると?
    • Collectionを実装している全てのクラスは、forEachメソッドを実装しないと動かない!
    • デフォルトメソッドという具体的な実装をインタフェースに書けるようにすることで解決
interface Person{
    long getId();
    default String getName() {
        return "hogehoge";
    }
}
  • 上記の例では、抽象メソッドであるgetIdとデフォルトメソッドであるgetNameを持つ
    • Personインタフェースを実装するクラスは、getIdの実装を必ず提供しなければならない
    • しかし、getNameメソッドについては、デフォルトメソッドをそのまま使用するか、オーバーライドするかを選択できる
  • 古典的なパターンでは、インタフェースとそのインタフェースのほぼ全てのメソッドを実装した抽象クラスを提供
    • CollectionとAbstractCollectionなど
    • 現在では、メソッドをインタフェース内に実装するだけでOK
インタフェースでのstaticメソッド
  • Java8ではインタフェースにstaticメソッドを追加できる
    • 今まではコンパニオンクラス(インタフェースと対になるクラス)にstaticメソッドを書くのが普通
    • CollectionとCollections、PathとPaths…
    • Java8ではインタフェースにstaticメソッドを追加を追加すればOK