Java 8のlambadaベストプラクティス動力ノードJava学院の整理

7531 ワード

8の中でLamdaは一番人気のあるテーマです。文法の変化だけではなく、もっと重要なのは関数式プログラミングの思想を持ってきました。優秀なプログラマーは関数式プログラミングの思想を勉強して、考え方を広くする必要があると思います。ですから、この文章はLambandの応用シーンや性能について話します。
JavaはなぜLambadaが必要ですか?
1996年1月にJava 1.0が発表されました。その後、コンピュータプログラミングの分野では天地を覆すような変化が発生しました。ビジネスの発展にはより複雑なアプリケーションが必要で、多くのプログラムはより強力なマルチコアCPUを装備したマシンを走っています。効率的な運行期間のコンパイラを持つJava仮想マシン(JVM)の出現により、プログラマはより多くのエネルギーを、各CPUクロック、各バイトのメモリをどのように使うかを考えるのではなく、より清潔でメンテナンスしやすいコードに配置することができます。
多核CPUの出現は「部屋の象」となり、無視できないが正視する人はいない。アルゴリズムにロックを導入すると、エラーが発生しやすく、時間がかかります。人々はjava.util.co ncurrentパッケージと多くの第三者クラスを開発し、同時に抽象化しようとして、プログラマがマルチコアCPU上で良好なプログラムを実行するように書くのを手伝っています。残念なことに、今まで歩いてきたのはまだまだです。
これらのライブラリの開発者はJavaを使う時、抽象的なレベルがまだ足りないことを発見しました。大きなデータを処理するのは良い例です。大きなデータに対して、Javaは効率的な並列操作に欠けています。Java 8は、開発者が複雑なセット処理アルゴリズムを作成することを可能にし、一つの方法を簡単に修正するだけで、コードをマルチコアCPU上で効率的に実行することができる。これらの大きなデータを並行して扱うクラスを作成するには、既存のJavaを言語レベルで修正する必要があります。
もちろん、このようにするのは代価があるので、プログラマーはどのようにlamband表現を含むコードを編纂して読むべきです。しかし、これは損をする商売ではありません。手书きの大きな段の复雑で、スレッドの安全なコードと比べて、新しい文法やいくつかの新しい习惯を学ぶのは简単です。企業レベルのアプリケーションを開発する時、良いクラスとフレームワークは開発時間とコストを大幅に低減し、使いやすく、効率的なクラスのライブラリを開発する障害も一掃しました。
ランバーの応用シーン
以下は関数プログラミングの実用性に重点を置いて、ほとんどのプログラマに理解され使用される技術を含めて、どのように良いコードを書くかに関心を持っています。
1.1.1.匿名クラスの代わりに()-{}を使う
今はRunnableスレッド、Swing、JavaFXのイベントモニターコードなどが、java 8では、醜い匿名クラスの代わりにLambada表現を使うことができます。

//Before Java 8:
new Thread(new Runnable() {
 @Override
 public void run() {
  System.out.println("Before Java8 ");
 }
}).start();
//Java 8 way:
new Thread(() -> System.out.println("In Java8!"));
// Before Java 8:
JButton show = new JButton("Show");
show.addActionListener(new ActionListener() {
  @Override
  public void actionPerformed(ActionEvent e) {
   System.out.println("without lambda expression is boring");
  }
  });
// Java 8 way:
show.addActionListener((e) -> {
 System.out.println("Action !! Lambda expressions Rocks");
});
外部サイクルの代わりに内サイクルを使う
外循環:どのようにするかを説明して、コードの中で2つ以上のfor循環を入れ込んでいるのはすべて読みにくいです。List中の要素のみを順次処理できます。
内循環:どうするかを説明します。必ずしも順番にListの要素を処理する必要はない。

//Prior Java 8 :
List features = Arrays.asList("Lambdas", "Default Method", 
"Stream API", "Date and Time API");
for (String feature : features) {
 System.out.println(feature);
}
//In Java 8:
List features = Arrays.asList("Lambdas", "Default Method", "Stream API",
 "Date and Time API");
features.forEach(n -> System.out.println(n));
// Even better use Method reference feature of Java 8
// method reference is denoted by :: (double colon) operator
// looks similar to score resolution operator of C++
features.forEach(System.out::println);

Output:
Lambdas
Default Method
Stream API
Date and Time API
サポート関数プログラミング
関数プログラミングをサポートするために、Java 8は、新しいパケットjava.util.functionに参加しています。ここでは、インターフェースjava.util.function.PrdicateはLambada関数プログラミングをサポートしています。

public static void main(args[]){
 List languages = Arrays.asList("Java", "Scala", "C++", "Haskell", "Lisp");
 System.out.println("Languages which starts with J :");
 filter(languages, (str)->str.startsWith("J"));
 System.out.println("Languages which ends with a ");
 filter(languages, (str)->str.endsWith("a"));
 System.out.println("Print all languages :");
 filter(languages, (str)->true);
 System.out.println("Print no language : ");
 filter(languages, (str)->false);
 System.out.println("Print language whose length greater than 4:");
 filter(languages, (str)->str.length() > 4);
}
 public static void filter(List names, Predicate condition) {
 names.stream().filter((name) -> (condition.test(name)))
  .forEach((name) -> {System.out.println(name + " ");
 });
 }

Output:
Languages which starts with J :
Java
Languages which ends with a
Java
Scala
Print all languages :
Java
Scala
C++
Haskell
Lisp
Print no language :
Print language whose length greater than 4:
Scala
Haskell
データを処理しますか?パイプの方式でもっと簡潔になります。
Java 8に追加されたStream APIは、集合中のデータをより便利に処理し、より高い性能と読み取り可能性を持つようにする。
一つの業務シーンを仮定します。20元以上の商品に対して、10%の割引処理を行い、これらの商品の割引価格を得ます。

final BigDecimal totalOfDiscountedPrices = prices.stream()
.filter(price -> price.compareTo(BigDecimal.valueOf(20)) > 0)
.map(price -> price.multiply(BigDecimal.valueOf(0.9)))
.reduce(BigDecimal.ZERO,BigDecimal::add);
System.out.println("Total of discounted prices: " + totalOfDiscountedPrices);
これらのデータを対象に処理すると、どれぐらいの行が必要ですか?何回循環しますか?いくつの中間変数を宣言しますか?
Lambadaの暗い面
前にはLambadaがJavaプログラマーの考え方をどう変えるかという話がありましたが、Lambadaも確かに困惑しています。
JVMは任意の言語で作成されたコードを実行できます。バイトコード自体は十分OOであり、Java言語に近いように設計されています。これはJavaがコンパイルされたバイトコードが容易に再編成されることを意味します。
しかし、Java言語ではない場合、格差はますます大きくなります。Scalソースとコンパイルされたバイトコードの間には大きな差があります。コンパイラは大量の合成種類の方法と変数を加えて、JVMを言語自体の特定の文法と流れに従って実行させます。
まずJava 6/7の伝統的な方法の例を見ます。

// simple check against empty strings
public static int check(String s) {
 if (s.equals("")) {
  throw new IllegalArgumentException();
 }
 return s.length();
}
//map names to lengths
List lengths = new ArrayList();
for (String name : Arrays.asList(args)) {
 lengths.add(check(name));
}
空の文字列が入ってきたら、このコードはエラーを投げます。スタックは次のように追跡します。

at LmbdaMain.check(LmbdaMain.java:19)
at LmbdaMain.main(LmbdaMain.java:34)
ラムダンの例を見てみます。

Stream lengths = names.stream().map(name -> check(name));
 at LmbdaMain.check(LmbdaMain.java:19)
at LmbdaMain.lambda$0(LmbdaMain.java:37)
at LmbdaMain$$Lambda$1/821270929.apply(Unknown Source)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.LongPipeline.reduce(LongPipeline.java:438)
at java.util.stream.LongPipeline.sum(LongPipeline.java:396)
at java.util.stream.ReferencePipeline.count(ReferencePipeline.java:526)
at LmbdaMain.main(LmbdaMain.java:39)
 これは非常にScalaに似ています。エラースタックの情報は長すぎて、コードの簡素化のために力の代価を払います。より正確なコードはより複雑なデバッグを意味します。
でも、それは影響しません。私たちはラムダンが好きです。
締め括りをつける
Javaの世界では、対象に向かっているのか主流に向かっているのか、対象に向かってプログラミングすることに慣れた開発者にとって、抽象的な概念は珍しくない。オブジェクト指向プログラミングはデータを抽象化し、関数式プログラミングは動作を抽象化することです。現実の世界では、データと行為が共存しており、プログラムもこのようにしています。
この新しい抽象的な方法には他の利点がある。多くの人は常に性能優先コードを作成していません。これらの人にとって、関数式プログラミングのメリットは特に大きいです。プログラマはより読みやすいコードを作成できます。このコードはより多くの業務ロジックを表現しています。読みやすいコードも維持しやすく、信頼性が高く、間違いにくいです。
コールバック関数とイベントプロセッサを書くとき、プログラマは、匿名内部クラスの煩雑さと可読性につきまとう必要はなく、関数的プログラミングは、イベント処理システムをより簡単にする。関数を便利に伝えることができますし、不活性コードの作成も容易になります。本当に必要なときだけ変数の値を初期化します。
とにかく、Javaは完璧に近いです。