Java8が最近リリースされ、その新しいラムダ式が本当にクールに見えるので、これが私たちが慣れていたAnonymousクラスの終meansを意味するのかどうか疑問に思いました。
私はこれについて少し研究しており、Lambda式がそれらのクラスを体系的に置き換える方法に関するいくつかのクールな例を見つけました。たとえば、Comparatorの匿名インスタンスを取得してソートを実行するコレクションのソートメソッドなどです。
Collections.sort(personList, new Comparator<Person>(){
public int compare(Person p1, Person p2){
return p1.firstName.compareTo(p2.firstName);
}
});
Lambdasを使用して次のことができます。
Collections.sort(personList, (Person p1, Person p2) -> p1.firstName.compareTo(p2.firstName));
そして、驚くほど簡潔に見えます。だから私の質問は、Lambdasの代わりにJava8でそれらのクラスを使い続ける理由はありますか?
編集
同じ質問ですが、反対方向に、匿名クラスの代わりにラムダを使用する利点は何ですか?ラムダは単一のメソッドインターフェイスでのみ使用できるため、この新しい機能はいくつかの場合にのみ使用されるショートカットですか、それとも本当に便利ですか?
匿名内部クラス(AIC)を使用して、抽象クラスまたは具象クラスのサブクラスを作成できます。 AICは、状態(フィールド)の追加を含む、インターフェイスの具体的な実装も提供できます。 AICのインスタンスは、そのメソッド本体でthis
を使用して参照できるため、その上でさらにメソッドを呼び出すことができ、その状態を経時的に変化させることができます。これらはいずれもラムダに適用されません。
AICの使用の大部分は、単一関数のステートレス実装を提供するためであり、ラムダ式に置き換えることができると思いますが、ラムダを使用できないAICの用途は他にもあります。 AICはここにあります。
UPDATE
AICとラムダ式のもう1つの違いは、AICが新しいスコープを導入することです。つまり、名前はAICのスーパークラスとインターフェイスから解決され、語彙的に囲まれた環境で発生する名前を隠すことができます。ラムダの場合、すべての名前は字句的に解決されます。
ラムダはすばらしい機能ですが、SAMタイプでのみ動作します。つまり、単一の抽象メソッドのみとインターフェースします。インターフェイスに複数の抽象メソッドが含まれるとすぐに失敗します。そこで匿名クラスが役立ちます。
したがって、匿名クラスを無視することはできません。ちなみに、p1
とp2
の型宣言をスキップすることで、sort()
メソッドをより単純化できます。
Collections.sort(personList, (p1, p2) -> p1.firstName.compareTo(p2.firstName));
ここでメソッドリファレンスを使用することもできます。 Person
クラスにcompareByFirstName()
メソッドを追加して、以下を使用します。
Collections.sort(personList, Person::compareByFirstName);
または、firstName
のゲッターを追加し、 Comparator.comparing()
メソッドからComparator
を直接取得します。
Collections.sort(personList, Comparator.comparing(Person::getFirstName));
匿名クラスでのラムダパフォーマンス
アプリケーションを起動すると、各クラスファイルをロードして検証する必要があります。
匿名クラスは、特定のクラスまたはインターフェイスの新しいサブタイプとしてコンパイラによって処理されるため、それぞれに対して新しいクラスファイルが生成されます。
ラムダはバイトコードの生成が異なり、より効率的で、JDK7に付属のinvokedynamic命令を使用します。
Lambdasの場合、この命令はバイトコードのラムダ式の変換を実行時まで遅らせるために使用されます。 (命令は初めてのみ呼び出されます)
結果として、Lambda式は静的メソッドになります(実行時に作成されます)。 (statelesとstatefullの場合にはわずかな違いがあり、それらは生成されたメソッド引数によって解決されます)
次の違いがあります。
1)構文
ラムダ式は、匿名内部クラス(AIC)と比較してきれいに見えます
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("in run");
}
};
Thread t = new Thread(r);
t.start();
}
//syntax of lambda expression
public static void main(String[] args) {
Runnable r = ()->{System.out.println("in run");};
Thread t = new Thread(r);
t.start();
}
2)スコープ
匿名の内部クラスはクラスです。つまり、内部クラス内で定義された変数のスコープを持っています。
一方、lambda式はそれ自体のスコープではなく、囲みスコープの一部です。
内部の匿名内部クラスを使用する場合、superおよびthisキーワードにも同様のルールが適用されますおよびラムダ式。匿名の内部クラスの場合、このキーワードはローカルスコープを指し、スーパーキーワードは匿名クラスのスーパークラスを指します。一方、ラムダ式の場合、このキーワードは囲んでいる型のオブジェクトを指し、スーパーは囲んでいるクラスのスーパークラスを指します。
//AIC
public static void main(String[] args) {
final int cnt = 0;
Runnable r = new Runnable() {
@Override
public void run() {
int cnt = 5;
System.out.println("in run" + cnt);
}
};
Thread t = new Thread(r);
t.start();
}
//Lambda
public static void main(String[] args) {
final int cnt = 0;
Runnable r = ()->{
int cnt = 5; //compilation error
System.out.println("in run"+cnt);};
Thread t = new Thread(r);
t.start();
}
3)パフォーマンス
実行時に、匿名の内部クラスはクラスの読み込み、メモリの割り当て、オブジェクトの初期化、非静的メソッドの呼び出しを必要としますが、ラムダ式は純粋なコンパイル時のアクティビティであり、実行時に余分なコストは発生しません。したがって、ラムダ式のパフォーマンスは、匿名の内部クラスと比較して優れています。**
**この点が完全に真実ではないことを理解しています。詳細については、次の質問を参照してください。 Lambdaと匿名の内部クラスのパフォーマンス:ClassLoaderの負荷を減らしますか?
Java 8のラムダは、関数型プログラミングのために導入されました。定型コードを回避できる場所。ラムダに関するこの興味深い記事に出会いました。
http://radar.oreilly.com/2014/04/whats-new-in-Java-8-lambdas.html
単純なロジックにはラムダ関数を使用することをお勧めします。ラムダを使用して複雑なロジックを実装すると、問題が発生した場合にコードをデバッグする際のオーバーヘッドになります。
ラムダ式と匿名クラスの違いを比較してみましょう。
1。構文
匿名クラス:
package com.onlyfullstack;
public class LambdaVsAnonymousClass {
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("Anonymous class");
}
};
Thread thread = new Thread(runnable);
thread.start();
}
}
ラムダ:
package com.onlyfullstack;
public class LambdaVsAnonymousClass {
public static void main(String[] args) {
Runnable runnable = () -> System.out.println("Lambda Expression");
Thread thread = new Thread(runnable);
thread.start();
}
}
2。実装
匿名クラスを使用して、任意の数の抽象メソッドを持つ任意のインターフェイスを実装できます。 Lambda ExpressionはSAM(Single Abstract Method)タイプでのみ動作します。これは、機能インターフェースとも呼ばれる単一の抽象メソッドのみを持つインターフェースです。インターフェイスに複数の抽象メソッドが含まれるとすぐに失敗します。
3。コンパイル
匿名クラス:JavaはLambdaVsAnonymousClass Javaファイルの2つのクラスファイルをLambdaVsAnonymousClass.classとして作成します。メインプログラムLambdaVsAnonymousClass $ 1.classを含みます–匿名クラスを含みます
Lambda Expression:Lambda式コンパイラでは、次のように1つのクラスファイルのみが作成されます。
したがって、Javaは使用される各匿名クラスの新しいクラスファイルを作成します。
4。パフォーマンス
匿名クラスは、指定されたクラスまたはインターフェースの新しいサブタイプとしてコンパイラーによって処理されるため、使用される各匿名クラスの新しいクラスファイルが生成されます。アプリケーションが起動すると、匿名クラス用に作成された各クラスがロードされ、検証されます。匿名クラスが多数ある場合、このプロセスは非常に時間がかかります。
ラムダ式ラムダの直接バイトコードを生成する代わりに(提案された匿名クラス構文シュガーアプローチのように)、コンパイラーは(invokeDynamic命令を介して)レシピを宣言し、実際の構築アプローチをランタイムに委任します。
そのため、ラムダ式は、呼び出されたときにのみ実行されるため、匿名クラスよりも高速です。
詳細については、以下のリンクを参照してください。
https://onlyfullstack.blogspot.com/2019/02/lambda-vs-anonymous-class-in-Java-8.html
https://onlyfullstack.blogspot.com/2019/02/how-lambda-internally-works-in-Java-8.html
invoke dynamic
を使用することで、ラムダは匿名クラスに変換されないコンパイル時に(Javaはオブジェクトの作成を行う必要がなく、メソッドのシグネチャに注意するだけで、メソッドにバインドできます)オブジェクトを作成せずにラムダは単一の抽象メソッドを持つ関数に適しているため、匿名クラスは残りますが、他のすべての場合には匿名内部クラスがあなたの救世主です。