java 8の新しい特性の一つ:lambada表現

11742 ワード

lambada表現はJava 8が持ってきたいくつかのヘビー級の新しい特性の一つで、lamban表現を借りて、Javaプログラムの設計をもっと簡潔にすることができます。本論文はJava 8の新特性の第一編であり、行動パラメータ化、lambada表現式、及び方法引用を検討する。
  • 予約待ち
  • lambada表現はjava 8が持ってきたいくつかのヘビー級の新しい特性の一つで、lamban表現を借りて、javaプログラムの設計をもっと簡潔にすることができます。最近の新しいプロジェクトは1.6のバージョンを捨てて、全面的にjava 8に基づいて開発して、本文はjava 8の新しい特性の第1編で、行為のパラメーター化、lambadaの表現式を探求して、および方法は引用します。
    一.行動パラメータ化
    行動パラメータ化は簡単で、関数の本体はテンプレート類共通コードだけを含んでいますが、業務シーンによって変化する論理はパラメータの形で関数に伝達されます。行動パラメータ化は、頻繁な変更の要求に応じてプログラムをより汎用的にすることができます。
    ビジネスシーンを考慮して、もし私たちがプログラムを通してアップルを選別する必要があるとしたら、まずアップルの本体を定義します。
    /**
     *     
     *
     * @author zhenchao.wang 2016-09-17 12:49
     * @version 1.0.0
     */
    public class Apple {
    
        /**    */
        private long id;
    
        /**    */
        private Color color;
    
        /**    */
        private float weight;
    
        /**    */
        private String origin;
    
        public Apple() {
        }
    
        public Apple(long id, Color color, float weight, String origin) {
            this.id = id;
            this.color = color;
            this.weight = weight;
            this.origin = origin;
        }
    
        //   getter setter
    }
    ユーザーの最初の需要は単にプログラムを通して緑のリンゴを選別することができるだけかもしれません。
    /**
     *      
     *
     * @param apples
     * @return
     */
    public static List filterGreenApples(List apples) {
        List filterApples = new ArrayList<>();
        for (final Apple apple : apples) {
            if (Color.GREEN.equals(apple.getColor())) {
                filterApples.add(apple);
            }
        }
        return filterApples;
    }
    時間が経ったら、ユーザーが新しい需要を出して、プログラムを通して赤いリンゴを選別したいです。そこで、私達はまた性に対して赤いリンゴを選別する機能を追加しました。
    /**
     *      
     *
     * @param apples
     * @return
     */
    public static List filterRedApples(List apples) {
        List filterApples = new ArrayList<>();
        for (final Apple apple : apples) {
            if (Color.RED.equals(apple.getColor())) {
                filterApples.add(apple);
            }
        }
        return filterApples;
    }
    より良い実装は、色をパラメータとして関数に伝達することであり、これにより、後にユーザが提示した様々な色選別要求に対応することができる。
    /**
     *        
     *
     * @param apples
     * @param color
     * @return
     */
    public static List filterApplesByColor(List apples, Color color) {
        List filterApples = new ArrayList<>();
        for (final Apple apple : apples) {
            if (color.equals(apple.getColor())) {
                filterApples.add(apple);
            }
        }
        return filterApples;
    }
    このように設計した後、ユーザーの色選別の需要が変化したことを心配しなくてもいいです。しかし、残念なことに、ある日ユーザーは重さがある基準に達するリンゴを選択できるように要求しました。
    /**
     *       ,       
     *
     * @param apples
     * @param color
     * @param weight
     * @return
     */
    public static List filterApplesByColorAndWeight(List apples, Color color, float weight) {
        List filterApples = new ArrayList<>();
        for (final Apple apple : apples) {
            if (color.equals(apple.getColor()) && apple.getWeight() >= weight) {
                filterApples.add(apple);
            }
        }
        return filterApples;
    }
    このようにパラメータを伝える方式は本当にいいですか?フィルタ条件がますます多くなると、結合モードがますます複雑になります。すべての状況を考慮して、それぞれの状況に対応する対策が必要ですか?そしてこれらの関数はフィルタ条件の部分だけが違っています。残りの部分は同じテンプレートコードです。 パラメータ化 ,関数にテンプレートコードだけを保持させ、フィルタ条件を抜き出してパラメータとして伝達させることは、java 8の前にフィルタインターフェースを定義することによって達成される。
    /**
     *       
     *
     * @author zhenchao.wang 2016-09-17 14:21
     * @version 1.0.0
     */
    @FunctionalInterface
    public interface AppleFilter {
    
        /**
         *       
         *
         * @param apple
         * @return
         */
        boolean accept(Apple apple);
    
    }
    
    /**
     *           
     *
     * @param apples
     * @param filter
     * @return
     */
    public static List filterApplesByAppleFilter(List apples, AppleFilter filter) {
        List filterApples = new ArrayList<>();
        for (final Apple apple : apples) {
            if (filter.accept(apple)) {
                filterApples.add(apple);
            }
        }
        return filterApples;
    }
    上記の挙動を抽象化した後、フィルタ条件を具体的に呼び出したところに設定し、条件をパラメータとして方法に渡すことができます。
    public static void main(String[] args) {
        List apples = new ArrayList<>();
    
        //     
        List filterApples = filterApplesByAppleFilter(apples, new AppleFilter() {
            @Override
            public boolean accept(Apple apple) {
                //       100g    
                return Color.RED.equals(apple.getColor()) && apple.getWeight() > 100;
            }
        });
    }
    上記の挙動パラメータ化方式は匿名クラスを用いて実現され、このような設計はjdk内部でもよく採用されています。例えばjava.util.Comparatorjava.util.concurrent.Callableなど、このようなインターフェースを使う時、私達は具体的に呼び出されたところで匿名クラスを使って関数の具体的な実行ロジックシリーズを指定することができます。java 8ではlambadaを通じて簡略化できます。
    //     
    List filterApples = filterApplesByAppleFilter(apples,
            (Apple apple) -> Color.RED.equals(apple.getColor()) && apple.getWeight() >= 100);
    lambada表現によって、コードを極めて簡潔にしました。javaのlamda表現を学びましょう。
    二.スランダ表現の定義
    私たちはラダ式を一つの表現として定義できます。 簡潔で伝達可能な匿名関数は、まずlamda表現が本質的に関数であることを明確にする必要があります。特定のクラスには属していませんが、パラメータリスト、関数本体、リターンタイプ、および異常を投げ出すことができます。次に匿名であり、具体的な関数名がない。lamban表現はパラメータのように伝達され、コードの作成が大幅に簡略化されます。フォーマットの定義は以下の通りです。
       :      ->    
       :      -> {     }
    なお、lamda表現にはreturnキーワードが含まれていますので、単一の表現では、明示的にreturnキーワードを書く必要はありませんが、表現が文セットである場合は、明示的にreturnを追加し、括弧{ }で複数の表達式を囲む必要があります。以下のいくつかの例を見ます。
    //          ,  return  
    (String s) -> s.length() 
    
    //     42     
    () -> 42 
    
    //        ,        
    (int x, int y) -> {
        int z = x * y;
        return x + z;
    }
    三.関数式インターフェースに依存してlamban表現を使う
    lamda表式の使用は関数式インターフェース、つまり関数式インターフェースだけで発生する場所を必要とします。lamda式を簡略化することができます。
    ユーザー定義の関数インターフェース
    関数式インターフェースは、専用として定義されています。 一つの抽象的な方法 のインターフェースです。java 8のインターフェース定義の改善は、デフォルトの方法を導入することで、インターフェースでは、方法にデフォルトの実装を提供することができますが、いくつかのデフォルトの方法があっても、一つの抽象的な方法だけがあれば、関数式インターフェースです。以下の通りです。
    /**
     *       
     *
     * @author zhenchao.wang 2016-09-17 14:21
     * @version 1.0.0
     */
    @FunctionalInterface
    public interface AppleFilter {
    
        /**
         *       
         *
         * @param apple
         * @return
         */
        boolean accept(Apple apple);
    
    }
    AppleFilterは、1つの抽象的な方法accept(Apple apple)だけを含み、定義に従って関数式インターフェースと見なしてもよく、定義においては、インターフェースに@FunctionalInterfaceの注釈を追加し、このインターフェースは関数式インターフェースとしてマークしてもよいが、このインターフェースを追加すると、コンパイラはこのインターフェースに抽象的な方法だけが許されるように制限する。この注釈を関数式インターフェースに追加することを推奨します。
    jdk独自の関数式インターフェース
    jdkはlamda式であり、関数式インターフェースが豊富に内蔵されています。
    関数インターフェース
    関数記述子
    オリジナルタイプの特化
    Prodcate
    T->bootlean
    IntPredicate、LongPredicate、Double Predicate
    Consmer
    T->void
    IntConsmer,LongConsmer,Double Consmer
    Function
    T->R
    IntFunction、Int ToDouble Function、IntToLongFunction、LongFunction…
    Supplier
    ()->T
    Boolean Supplier,Int Supplier,LongSupplier,Double Supplier
    UARyOperator
    T->T
    Int Uniary Operator,LongUniary Operator,Double Uniary Operator
    Binary Operator
    (T,T)->T
    IntBinary Operator,LongBinary Operator,Doubl Binary Operator
    BiPredicate
    (L,R)->bollan
     
    BiConsmer
    (T,U)->void
     
    BiFunction
    (T,U)->R
     
    次に、PredicateConsumerFunctionの使用例について説明する。
    Prodcate
    @FunctionalInterface
    public interface Predicate {
    
        /**
         * Evaluates this predicate on the given argument.
         *
         * @param t the input argument
         * @return {@code true} if the input argument matches the predicate,
         * otherwise {@code false}
         */
        boolean test(T t);
    }
    Predicateの機能は、上記のAppleFilterと同様であり、外部で設定された条件を用いて着信パラメータを検証し、検証結果booleanに戻り、次にListセットの要素をPredicateを用いてフィルタリングする。
    /**
     *                 
     *
     * @param list
     * @param predicate
     * @param 
     * @return
     */
    public  List filter(List list, Predicate predicate) {
        List newList = new ArrayList();
        for (final T t : list) {
            if (predicate.test(t)) {
                newList.add(t);
            }
        }
        return newList;
    }
    上の関数式インターフェースを使って、文字列セットの空の文字列をフィルタします。
    demo.filter(list, (String str) -> null != str && !str.isEmpty());
    Consmer
    @FunctionalInterface
    public interface Consumer {
    
        /**
         * Performs this operation on the given argument.
         *
         * @param t the input argument
         */
        void accept(T t);
    }
    Consmerは、パラメータを受信するaccept抽象関数を提供するが、値を返さない。次にConsumerエルゴードセットを利用する。
    /**
     *     ,       
     *
     * @param list
     * @param consumer
     * @param 
     */
    public  void filter(List list, Consumer consumer) {
        for (final T t : list) {
            consumer.accept(t);
        }
    }
    上の関数式インターフェースを使って、文字列のセットを巡回し、空でない文字列を印刷します。
    demo.filter(list, (String str) -> {
            if (StringUtils.isNotBlank(str)) {
                System.out.println(str);
            }
        });
    Function
    @FunctionalInterface
    public interface Function {
    
        /**
         * Applies this function to the given argument.
         *
         * @param t the function argument
         * @return the function result
         */
        R apply(T t);
    }
    Functionは、変換動作を実行し、入力はタイプTのデータであり、Rタイプのデータに戻り、次にFunctionを用いてセットを変換する。
    /**
     *     ,         
     *
     * @param list
     * @param function
     * @param 
     * @param 
     * @return
     */
    public  List filter(List list, Function function) {
        List newList = new ArrayList();
        for (final T t : list) {
            newList.add(function.apply(t));
        }
        return newList;
    }
    上の関数式インターフェースを利用して、パッケージ文字列(整数数字の文字列表示)のインターフェースを、整数集合に変換します。
    demo.filter(list, (String str) -> Integer.parseInt(str));
    これらの関数式インターフェースはまた、いくつかの論理操作のデフォルトの実装を提供しています。java 8インターフェースのデフォルトの方法を後で紹介します。
    使用中に注意しなければならないことがあります。
    タイプ推定
    コード化過程では、コードがどの関数式インターフェースに具体的にマッチするかという疑問があるかもしれませんが、実際にコンパイラはパラメータ、戻りタイプ、異常タイプ(存在すれば)などによって正確に判定します。具体的な呼び出し時には、パラメータの種類を省略することができ、コードをさらに簡略化することができます。
    //     
    List filterApples = filterApplesByAppleFilter(apples,
            (Apple apple) -> Color.RED.equals(apple.getColor()) && apple.getWeight() >= 100);
    
    //                  ,             
    List filterApples = filterApplesByAppleFilter(apples,
            apple -> Color.RED.equals(apple.getColor()) && apple.getWeight() >= 100);
    ローカル変数
    上記のすべての例において、私たちのlamda表現はその主体パラメータを使用しています。また、lamdaに局所変数を使用することもできます。
    int weight = 100;
    List filterApples = filterApplesByAppleFilter(apples,
            apple -> Color.RED.equals(apple.getColor()) && apple.getWeight() >= weight);
    この例ではlamdaで局所変数weightを使用しましたが、lamdaでは局所変数を使用するにはこの変数が必要です。 明示的な声明はfinalまたは事実上のfinalです。 ,これは主に、ローカル変数がスタックに格納されているため、lamda式は他のスレッドで実行され、このスレッドビューがこのローカル変数にアクセスすると、この変数は変更または回収される可能性があるので、finalで修飾した後、スレッドセキュリティの問題が存在しない。
    四.方法引用
    このような簡略化はコードをより直感的に見せることができます。まず例を見ます。
    /* ...   apples       */
    
    //   lambda   
    apples.sort((Apple a, Apple b) -> Float.compare(a.getWeight(), b.getWeight()));
    
    //       
    apples.sort(Comparator.comparing(Apple::getWeight));
    方法参照は、::によって方法の隷属及び方法自体を連結し、主に3つの種類に分類される。
    静的方法
    (args) -> ClassName.staticMethod(args)
       
    ClassName::staticMethod
    パラメータの実例的な方法
    (args) -> args.instanceMethod()
       
    ClassName::instanceMethod  // ClassName args   
    外部の実例的な方法
    (args) -> ext.instanceMethod(args)
       
    ext::instanceMethod(args)