JAvaの新しい特性の-関数式プログラミング(lambda)

3383 ワード

前の記事(https://www.jianshu.com/p/99d45dfae968 )、関数式プログラミングの基本思想、概念とJavaの関数式プログラミングに対する基本的な使用を理解した.
本編ではlambda式と関数式プログラミングの関係についてお話しします.
Java 8の場合、Lambda式は常に最初に言及された新しい特性です.実はlambda式が関数式プログラミングスタイルをJavaプラットフォームに導入し、Java開発者の効率を大幅に向上させたのです.
一、Lambdaを導入するタイミング
まず2つの異なるコードを見てみましょうが、同じ効果です.
1.lambda以前のコードがありません:
public class OldThread {
 public static void main(String[] args) {
   new Thread(new Runnable() {
     public void run() {
       System.out.println("Hello World!");
     }
   }).start();
 }
}

Java.lang.Runnableインタフェースのインプリメンテーションを使用して、新しいjava.lang.Threadオブジェクトを作成し、Threadオブジェクトのstartメソッドを呼び出して起動します.Runnableインタフェースは匿名の内部クラスによって実現される.
2.lambdaを使用した後のコード:
public class LambdaThread {
  public static void main(String[] args) {
    new Thread(() -> System.out.println("Hello World!")).start();
  }
}

Lambdaを使用すると、1行のコードだけで匿名クラスが必要になる前に完了することが分かったので、Lambda式は匿名内部クラスの構文糖を作成することです.コンパイラの助けで、開発者はより少ないコードで作業を完了することができます.
もちろんlambdaはそれだけではありません.
二、Lambdaの深い理解
2.1 lambda匿名クラスのタイプを推定する方法
初めてlambdaを使うのはワクワクしますが、Javaは強いタイプのプログラミング言語ではないでしょうか.Lambda式にはクラスのタイプ情報がなく、多くのクラスが同じlambda式で書くことができるようですが、コンパイラはどのようにlambda式の匿名クラスのタイプを推定しますか?
Lambda式のタイプは、コンパイラがそのコンテキスト環境に基づいてコンパイル時に推定します.
たとえば、Lambda式()->System.out.println(「Hello World!」)は、関数インタフェースの一意のメソッドがパラメータを受け入れず、戻り値がvoidである限り、関数インタフェースインスタンスを必要とするコンテキストに表示されます.これはRunnableインタフェースであるか、サードパーティ製ライブラリまたはアプリケーションコードからの他の関数インタフェースである可能性があります.コンテキスト環境によって決定されるタイプをターゲットタイプと呼びます.Lambda式は、異なるコンテキスト環境で異なるタイプを持つことができます.Lambda式のように、ターゲットタイプによってタイプが決定される式をマルチステート式と呼びます.
したがってlambdaを使用するには、次の点に注意する必要があります.
  • Lambda式の構文は柔軟で、Javaのメソッドに似ています.形式パラメータのリストと本体があります.
  • パラメータのタイプはオプションです.タイプを指定しない場合は、コンパイラによってコンテキスト環境によって推定されます.
  • Lambda式のボディは、値またはvoidを返すことができる.戻り値のタイプは、ターゲットタイプと一致する必要があります.
  • Lambda式のマスターが例外を放出した場合、例外のタイプはターゲットタイプのthrows宣言と一致する必要があります.
  • で曖昧な場合、複数のタイプが要求を満たす可能性があり、コンパイラは独自にタイプ推定を完了できない.この場合、コンパイラがタイプ推定を完了するのに役立つようにコードを書き換える必要があります.例えば:
  • public class LambdaTargetType {
     
      @FunctionalInterface
      interface A {
        void a();
      }
     
      @FunctionalInterface
      interface B {
        void b();
      }
     
      class UseAB {
        void use(A a) {
          System.out.println("Use A");
        }
     
        void use(B b) {
          System.out.println("Use B");
        }
      }
     
      void targetType() {
        UseAB useAB = new UseAB();
        A a = () -> System.out.println("Use");
        useAB.use(a);
      }
    }
    

    ここでUseABクラスにはマルチステート方式useがあり、lambda式で返される関数タイプをAと指定する必要があります.
    2.2変数の役割ドメイン
    Lambda式のボディでは、コンテキスト環境の変数を参照する必要があることがよくあります.Lambda式は単純なポリシーを使用して変数の役割ドメインを使用します.多くの言語の閉パッケージとは異なり、Lambda式には新しいネーミングドメイン(scope)は導入されていません.Lambda式の変数名は、そのコンテキスト環境と同じ変数ドメインにあります.Lambda式が実行されると、これらの変数がlambda式に自動的に導入されることに相当します.
    したがって,Lambda式のthisもそれを囲むコードの意味と同じである.
    次のコードでは、Lambda式の本体に、それを囲むコンテキスト環境からの変数nameが参照されます.
    public void run() {
      String name = "Alex";
      new Thread(() -> System.out.println("Hello, " + name)).start();
    }
    

    Lambda式で参照できる変数はfinalとして宣言するか、実際にfinal(effectively final)として宣言する必要があります.実際にfinalは変数がfinalと宣言されていないが、初期化後は付与されていないことを意味する.したがって、変数の値は変更されません.
    三、まとめ
    Java 8が導入したLambda式とストリーム処理は開発効率を極めて高める重要な特性である.各Java開発者は、それらの使用を熟練しなければならない.同時にLambda式をより深く理解し、それを知るにはその理由を知る必要がある.
    end完