JAva 8のlambda式(デフォルトメソッド)
4272 ワード
多くの開発言語では、関数式がコレクションライブラリに統合されています.これにより、ループ方式に必要なコードよりも少なく、理解しやすくなります.次のようなループを例にとります.
実際にはもっと良い方法があります.API開発者は、集合の各要素に関数を適用するためのforEachメソッドを提供することができる.次は、この方法で作成された簡単な呼び出しです.
コレクションライブラリが完全に再設計されている場合は、問題はありません.しかし,Javaの集合ライブラリは数年前に設計されており,これは問題をもたらす.CollectionインタフェースにforEachなどの新しいメソッドが追加された場合、Collectionインタフェースを実装したカスタムクラスごとにこのメソッドを実装する必要があります.これはjavaでは全く受け入れられません.
JAva設計者たちは、インタフェースに具体的な実装方法(デフォルト方法と呼ばれる)を含めることを許可することによって、この問題を永遠に解決することを望んでいる.これらの方法は、既存のインタフェースに安全に追加することができる.ここでは、デフォルトの方法について詳しく説明します.注意:Java 8では、forEachメソッドがIterableインタフェースに追加されています(Collectionインタフェースの親インタフェースです).次のインタフェースがあるとします.
このインタフェースには、抽象メソッドgetIdとデフォルトメソッドgetNameの2つのメソッドがあります.もちろん、Personインタフェースを実装する特定のクラスはgetIdメソッドを実装する必要がありますが、getNameの実装を保持するか、書き換えるかを選択できます.
デフォルトの方法は、従来の古典的なモードを終了します.すなわち、インタフェースと、インタフェースを実装するほとんどまたはすべての方法の抽象クラス、例えば、Collection/ABstractCollectionまたはWindowListener/WindowAdapterを提供する.インタフェースでそれらの方法を実現するだけです.
1つのインタフェースにデフォルトのメソッドが定義され、別の親またはインタフェースに同じ名前のメソッドが定義されている場合、どちらを選択しますか?scalaやC++などの言語には複雑なルールがあるかもしれませんが、Javaのルールは以下のように簡単です.親のメソッドを選択します.親クラスが特定のインプリメンテーションメソッドを提供する場合、インタフェースに同じ名前とパラメータを持つデフォルトのメソッドは無視されます. インタフェースが競合しています.親インタフェースがデフォルトメソッドを提供し、別のインタフェースが同じ名前とパラメータタイプのメソッドを提供している場合(デフォルトメソッドかどうかにかかわらず)、このメソッドを上書きすることで競合を解決する必要があります.
第2のルールを詳しく理解してみましょう.別のインタフェースにもgetNameというメソッドが含まれているとします.
もしあなたがこの2つのインタフェースを同時に実現するクラスを作成したら、何が起こりますか?
クラスはPersonとNameインタフェースが同時に提供するgetNameメソッドを継承しますが、この2つのメソッドの実装は一致しません.Javaコンパイラはエラーを報告し、開発者にこの競合を解決し、そのうちの1つを自動的に選択することはありません.この場合、次のようにインタフェースを呼び出す方法を選択するには、StudentクラスにgetNameメソッドを指定します.
NamedインタフェースはgetNameメソッドのデフォルト実装を提供していないと仮定します.
もしそうなら、StudioクラスはPersonインタフェースのデフォルトメソッドを継承できますか?そうかもしれないが、Javaデザイナーたちは統一を保つために、以前と同じ処理方法を選んだ.2つのインタフェースがどのように衝突するかは重要ではありません.1つのインタフェースが実装されている限り、コンパイラはエラーを報告し、開発者は手動でこの衝突を解決する必要があります.注意:もちろん、両方のインタフェースが共有メソッドにデフォルトの実装を提供していない場合は、Java 8以前の状況に戻り、競合はありません.
次に、親クラスを継承し、インタフェースを実装するクラスを考えます.この親クラスとインタフェースには同じ名前の方法があります.たとえば、Personはクラスであると仮定し、Studentクラスの定義は次のようになります.
この場合、親クラスのメソッドのみが機能し、インタフェースのデフォルトのメソッドは無視されます.この例では、NameインタフェースのgetNameメソッドがデフォルトメソッドであるかどうかにかかわらず、StudioはPersonクラスのgetNameメソッドを継承します.これが「クラス優先」のルールです.クラス優先のルールは、Java 7の互換性を保証します.インタフェースにデフォルトのメソッドを追加した場合、Java 8が以前に作成したコードには影響しません.例:
for(int i = 0; i < list.size(); i++)
System.out.println(list.get(i));
実際にはもっと良い方法があります.API開発者は、集合の各要素に関数を適用するためのforEachメソッドを提供することができる.次は、この方法で作成された簡単な呼び出しです.
package java8test;
import java.util.Arrays;
import java.util.List;
public class T3 {
public static void main(String[] args) {
List<String> list = Arrays.asList("a","b","c","d","e","f");
//
list.forEach(System.out::println);
}
}
コレクションライブラリが完全に再設計されている場合は、問題はありません.しかし,Javaの集合ライブラリは数年前に設計されており,これは問題をもたらす.CollectionインタフェースにforEachなどの新しいメソッドが追加された場合、Collectionインタフェースを実装したカスタムクラスごとにこのメソッドを実装する必要があります.これはjavaでは全く受け入れられません.
JAva設計者たちは、インタフェースに具体的な実装方法(デフォルト方法と呼ばれる)を含めることを許可することによって、この問題を永遠に解決することを望んでいる.これらの方法は、既存のインタフェースに安全に追加することができる.ここでは、デフォルトの方法について詳しく説明します.注意:Java 8では、forEachメソッドがIterableインタフェースに追加されています(Collectionインタフェースの親インタフェースです).次のインタフェースがあるとします.
interface Person {
long getId();
// default
default String getName(){
return "John Q. Public";
}
}
このインタフェースには、抽象メソッドgetIdとデフォルトメソッドgetNameの2つのメソッドがあります.もちろん、Personインタフェースを実装する特定のクラスはgetIdメソッドを実装する必要がありますが、getNameの実装を保持するか、書き換えるかを選択できます.
デフォルトの方法は、従来の古典的なモードを終了します.すなわち、インタフェースと、インタフェースを実装するほとんどまたはすべての方法の抽象クラス、例えば、Collection/ABstractCollectionまたはWindowListener/WindowAdapterを提供する.インタフェースでそれらの方法を実現するだけです.
1つのインタフェースにデフォルトのメソッドが定義され、別の親またはインタフェースに同じ名前のメソッドが定義されている場合、どちらを選択しますか?scalaやC++などの言語には複雑なルールがあるかもしれませんが、Javaのルールは以下のように簡単です.
第2のルールを詳しく理解してみましょう.別のインタフェースにもgetNameというメソッドが含まれているとします.
interface Named{
default String getName(){
return getClass().getName() + "_" + hashCode();
}
}
もしあなたがこの2つのインタフェースを同時に実現するクラスを作成したら、何が起こりますか?
class Student implements Person,Named {
......
}
クラスはPersonとNameインタフェースが同時に提供するgetNameメソッドを継承しますが、この2つのメソッドの実装は一致しません.Javaコンパイラはエラーを報告し、開発者にこの競合を解決し、そのうちの1つを自動的に選択することはありません.この場合、次のようにインタフェースを呼び出す方法を選択するには、StudentクラスにgetNameメソッドを指定します.
interface Person{
long getId();
default String getName(){
return "John Q. Public";
}
}
interface Named{
default String getName(){
return getClass().getName() + "_" + hashCode();
}
}
class Student implements Person,Named{
@Override
public long getId() {
return 0;
}
public String getName(){
// :Person.super.getName()
return Person.super.getName();
}
}
NamedインタフェースはgetNameメソッドのデフォルト実装を提供していないと仮定します.
interface Named{
String getname();
}
もしそうなら、StudioクラスはPersonインタフェースのデフォルトメソッドを継承できますか?そうかもしれないが、Javaデザイナーたちは統一を保つために、以前と同じ処理方法を選んだ.2つのインタフェースがどのように衝突するかは重要ではありません.1つのインタフェースが実装されている限り、コンパイラはエラーを報告し、開発者は手動でこの衝突を解決する必要があります.注意:もちろん、両方のインタフェースが共有メソッドにデフォルトの実装を提供していない場合は、Java 8以前の状況に戻り、競合はありません.
次に、親クラスを継承し、インタフェースを実装するクラスを考えます.この親クラスとインタフェースには同じ名前の方法があります.たとえば、Personはクラスであると仮定し、Studentクラスの定義は次のようになります.
class Student extends Person implements Named {......}
この場合、親クラスのメソッドのみが機能し、インタフェースのデフォルトのメソッドは無視されます.この例では、NameインタフェースのgetNameメソッドがデフォルトメソッドであるかどうかにかかわらず、StudioはPersonクラスのgetNameメソッドを継承します.これが「クラス優先」のルールです.クラス優先のルールは、Java 7の互換性を保証します.インタフェースにデフォルトのメソッドを追加した場合、Java 8が以前に作成したコードには影響しません.例:
package java8test;
public class T4 {
public static void main(String[] args) {
Student stu = new Student();
// , :John Q. Public
System.out.println(stu.getName());
}
}
class Person{
public long getId(){
return 100L;
};
public String getName(){
return "John Q. Public";
}
}
interface Named{
default String getName(){
return getClass().getName() + "_" + hashCode();
}
}
class Student extends Person implements Named{}