Java 8関数インタフェース、lambda式、メソッド、コンストラクタリファレンス


関数インタフェースの使用背景
Javaはオブジェクト向けプログラミング言語であり、javaのすべてはオブジェクト向け(元のデータ型を除く)であることが知られています.Javaでは関数(メソッド)はクラス/オブジェクトの一部であり、単独では存在しません.C++、Javascriptなどの他の関数プログラミング言語では、個別の関数を記述し、直接呼び出すことができます.
オブジェクト向けが悪いわけではありませんが、冗長なコードを書く必要がある場合があります.簡単な例を挙げると、Runnableインスタンスを作成する必要があります.通常、匿名の内部クラスは次のように使用されます.
Runnable r = new Runnable(){
            @Override
            public void run() {
                System.out.println("My Runnable");
            }};

実はこのコードでは、実際に役に立つのは内部のrunメソッドだけで、他のコードはjavaがオブジェクトに要求しているだけです.
JAva 8関数インタフェースとlambda式は、少量のコードを書くことで上記の効果を達成することができます.
JAva 8関数インタフェース
Java 8では、抽象的なメソッドが1つしかないインタフェースを関数インタフェースと呼び、@FunctionalInterface注釈を使用してインタフェースが関数インタフェースであることを示すことができます.この注釈は必須ではありません.注釈を加えると、インタフェースに複数の抽象メソッドがある場合、コンパイルエラーが表示されます.
JAva 8関数インタフェースの最大の利点は、lambda式を使用して関数インタフェースを初期化し、匿名の内部クラススタイルの重い書き方を避けることです.
JAva 8の集合APIは書き換えられ,多くの関数インタフェースを用いた新しいストリームAPIが導入された.Java.util.functionパッケージの下には、Consumer、Supplier、Function、Predicateなどの多くの関数インタフェースが定義されています.
Lambda式
lambda式によりjavaのオブジェクト向けに関数式プログラミングをイメージ化できます.オブジェクトはjava言語の基本であり、オブジェクトから離れて単独で使用することはできません.これもjavaがlambda式を提供して関数式インタフェースしか使用できない理由です.
抽象的な方法が1つしかない場合、lambda式を使用すると困惑することはありません.Lambda式の署名:*(argument 1,argument 2,...)->(body)*
  • *(argument 1,argument 2,...)*メソッド署名、argument 1,argument 2,...パラメータリスト
  • *->は矢印であり、方法体
  • を指す.
  • (body)はメソッド体であり、{}ラップコードブロックを用いて
  • を表すことができる.
  • 無参照メソッドの場合、メソッド署名は()
  • を使用することができる.
  • パラメータが1つしかない場合は、()は
  • を省略することができる.
    Runnableインスタンスを前に作成したコードは、lambda式を使用して実装できます.
    Runnable r1 = () -> System.out.println("My Runnable");

    このコードを説明します.
  • Runnableは関数インタフェースなので、lambda式を使用してインスタンス
  • を作成できます.
  • run()メソッドにはパラメータがあるので,我々のlambda式にもパラメータ
  • はない.
  • if-else文のように、コードが1行しかない場合は{}記号を省くことができます.

  • なぜlambda式を使うのか
    コード量を減らす
            lambda               
    

    連続的、並列的な実行をサポート
    lambda                 API         。
          ,      。          :
             ,        :
    
    private static boolean isPrime(int number) {        
        if(number < 2) return false;
        for(int i=2; iif(number % i == 0) return false;
        }
        return true;
    }

    この問題を解決するコードは連続しており,与えられた数字が大きいと時間がかかる.もう1つの欠点は、ブランチが多すぎて可読性が悪いことです.LambdaとストリーミングAPIの書き方:
    private static boolean isPrime(int number) {        
        return number > 1
                && IntStream.range(2, number).noneMatch(
                        index -> number % index == 0);
    }

    IntStreamは、元のタイプintの連続並列実行をサポートする自然な順序付け要素です.よりよく読むために、コードはさらに最適化されます.
    private static boolean isPrime(int number) {
        IntPredicate isDivisible = index -> number % index == 0;
    
        return number > 1
                && IntStream.range(2, number).noneMatch(
                        isDivisible);
    }

    range(arg 1,arg 2)メソッドは、1つのIntStreamがarg 1を含むが、arg 2のステップ長が1でないシーケンスを返す.
    noneMatch()は、要素が一致していないかどうか、所定の事前定義条件Predicateに一致しないかどうかを返します.
    メソッドに動作アクションを渡す
    リスト内の条件を満たす要素を合計する必要があります.
    public static int sumWithCondition(List numbers, Predicate predicate) {
            return numbers.parallelStream()
                    .filter(predicate)
                    .mapToInt(i -> i)
                    .sum();
        }

    使用方法は次のとおりです.
    //       
    sumWithCondition(numbers, n -> true)
    //         
    sumWithCondition(numbers, i -> i%2==0)
    //     5     
    sumWithCondition(numbers, i -> i>5)

    効率的なリロード
    もし私たちが3-11の間の最大の奇数を見つけて、その平方を求める必要があるならば.
    このようなコードを使用することができます.
    private static int findSquareOfMaxOdd(List numbers) {
            int max = 0;
            for (int i : numbers) {
                if (i % 2 != 0 && i > 3 && i < 11 && i > max) {
                    max = i;
                }
            }
            return max * max;
        }

    上記のコードは、1つのシーケンスで処理され、ストリームAPIを使用して代替することができます.
    public static int findSquareOfMaxOdd(List numbers) {
            return numbers.stream()
                    .filter(NumberTest::isOdd)  
                    .filter(NumberTest::isGreaterThan3)
                    .filter(NumberTest::isLessThan11)
                    .max(Comparator.naturalOrder())
                    .map(i -> i * i)
                    .get();
        }
    
        public static boolean isOdd(int i) {
            return i % 2 != 0;
        }
    
        public static boolean isGreaterThan3(int i){
            return i > 3;
        }
    
        public static boolean isLessThan11(int i){
            return i < 11;
        }

    コロン式はメソッドの参照であり、NumberTest::isOddは(i)->isOdd(i)またはi->NumberTest.isOdd(i)の略である.
    より多くのlambda式の例
    () -> {}                     //       
    
    () -> 42                     //       
    () -> null                   //       
    () -> { return 42; }         //   ,        
    () -> { System.gc(); }       // 
    
    //       
    () -> {
      if (true) return 10;
      else {
        int result = 15;
        for (int i = 1; i < 10; i++)
          result *= i;
        return result;
      }
    }                          
    
    (int x) -> x+1             //           
    (int x) -> { return x+1; } // 
    (x) -> x+1                 //     ,      
    x -> x+1                   //   
    
    (String s) -> s.length()   // 
    (Thread t) -> { t.start(); } // 
    s -> s.length()              //                
    t -> { t.start(); }          //                
    
    (int x, int y) -> x+y      //           
    (x,y) -> x+y               //             
    (x, final y) -> x+y        //   。    final  y
    (x, int y) -> x+y          //   ,        

    メソッド、コンストラクタリファレンス
    JAva 8は、コロン式を使用してメソッドを参照できます.
    System::getProperty
    System.out::println
    "abc"::length
    ArrayList::new
    int[]::new