web-dev-qa-db-ja.com

ラムダを使用するために機能的なインターフェイスが必要なのはなぜですか?

この質問はすでにどこかにあると思いますが、見つけることができませんでした。

なぜラムダを操作するための機能的なインターフェイスが必要なのか、理解できません。次の例を考えてみましょう。

public class Test {

    public static void main(String...args) {
        TestInterface i = () -> System.out.println("Hans");
//      i = (String a) -> System.out.println(a);

        i.hans();
//      i.hans("Hello");
    }
}

public interface TestInterface {
    public void hans();
//  public void hans(String a);
}

これは問題なく機能しますが、コメント行のコメントを外すと機能しません。どうして?私の理解では、コンパイラは入力パラメータが異なるため、2つのメソッドを区別できるはずです。なぜ機能的なインターフェイスが必要で、コードが爆発するのですか?

編集:リンクされた重複は私の質問に答えませんでした。なぜなら、私は異なるメソッドパラメータについて尋ねているからです。しかし、助けてくれたみんなに感謝します。 :)

EDIT2:申し訳ありませんが、私は明らかにネイティブスピーカーではありませんが、正確に言うと:

public interface TestInterface {
    public void hans();                 //has no input parameters</br>
    public void hans(String a);         //has 1 input parameter, type String</br>
    public void hans(String a, int b);  //has 2 input parameters, 1. type = String, 2. type = int</br>
    public void hans(int a, int b);     //has also 2 input parameters, but not the same and a different order than `hans(String a, int a);`, so you could distinguish both
}

public class Test {

    public static void main(String...args) {
        TestInterface i = () -> System.out.println("Hans");
        i = (String a) -> System.out.println(a);
        i = (String a, int b) -> System.out.println(a + b);
        i = (int a, int b) -> System.out.println(a);

        i.hans(2, 3);   //Which method would be called? Of course the one that would take 2 integer arguments. :)
    }
}

私が求めているのは、議論についてです。メソッド名は重要ではありませんが、各メソッドは異なる引数の一意の順序を取ります。そのため、Oracleは「Lambda-Interface」ごとに単一のメソッドを可能にする代わりにこの機能を実装できます。

32
codepleb

あなたが書くとき:

_TestInterface i = () -> System.out.println("Hans");
_

TestInterfacevoid hans()メソッドに実装を提供します。

ラムダ式を複数の抽象メソッドを持つインターフェイス(つまり、機能しないインターフェイス)に割り当てることができる場合、ラムダ式はメソッドの1つしか実装できず、他のメソッドは実装されません。

異なるシグネチャを持つ2つのラムダ式を同じ変数に割り当てることでは解決できません(2つのオブジェクトの参照を1つの変数に割り当て、その変数が両方のオブジェクトを一度に参照することを期待できないように)。

32
Eran

メソッドに1つのメソッドのみを含める必要がある最も重要な理由は、そうでなければ混乱が容易に発生する可能性があるためです。インターフェイスで複数のメソッドが許可されている場合、引数リストが同じ場合、ラムダはどのメソッドを選択する必要がありますか?

interface TestInterface {
    void first();
    void second(); // this is only distinguished from first() by method name
    String third(); // maybe you could say in this instance "well the return type is different"
    Object fourth(); // but a String is an Object, too !
}

void test() {
    // which method are you implementing, first or second ?
    TestInterface a = () -> System.out.println("Ido mein ado mein");
    // which method are you implementing, third or fourth ?
    TestInterface b = () -> "Ido mein ado mein";
}
21
blagae

匿名クラスを探しているようです。次のコードが機能します。

public class Test {

    public static void main(String...args) {
        TestInterface i = new TestInterface() {
            public void hans() {
                System.out.println("Hans");
            }
            public void hans(String a) {
                System.out.println(a);
            }
        };

        i.hans();
        i.hans("Hello");
    }
}

public interface TestInterface {
    public void hans();
    public void hans(String a);
}

ラムダ式は、ほとんどの場合、1つのメソッドのみで匿名クラスを記述するためのより短い方法です。 (同様に、匿名クラスは、1つの場所でのみ使用する内部クラスの省略形です)

5
immibis

ラムダ関数を作成するために、機能的なインターフェイスを作成する必要はありません。このインターフェースにより、将来の関数呼び出し用のインスタンスを作成できます。

あなたの場合は、既存のインターフェイスRunableを使用できます

Runnable r = () -> System.out.println("Hans");

そして、呼び出します

r.run();

ラムダ->の略記として:

Runnable r = new Runnable() {
     void run() {
          System.out.println("Hans");`
     }
}

ラムダを使用すると、上記の例の内部で作成される匿名クラスは不要です。

ただし、これにはいくつかの制限があります。ラムダで使用されるインターフェイスと呼ばれるメソッドを特定するには、SAM(Single Abstract Method)である必要があります。次に、メソッドが1つだけあります。

詳細な説明については、以下をお読みください。

機能的インターフェイスの紹介– Java 8 で再作成された概念

機能インターフェイスには、 Java仕様 に従って、抽象メソッドを1つだけ含めることができます。

ラムダ式はコメント付きコードと同じように一度だけ使用できますが、ラムダ式をパラメーターとして渡すことで関数コールバックを模倣する場合、変数データ型が機能インターフェースであるため、機能インターフェースが必須です。

たとえば、Runnableは組み込みの機能インターフェイスです。

public interface Runnable() {
    public void run();
}

使用法は次のように実証できます。

public class LambdaTest {
    // data type of parameter 'task' is functional interface 'Runnable'
    static void doSeveralTimes(Runnable task, int repeat) {
        for (int i = 0; i < repeat; i++) {
            task.run();
        }
    }

    public static void main(String[] args) {
        // one-time lambda
        doSeveralTimes(() -> {
            System.out.println("one-time lambda");
        }, 3);

        // lambda as variable
        Runnable test;
        test = () -> {
            System.out.println("lambda as variable");
        };
        doSeveralTimes(test, 3);
    }
}

結果は次のとおりです。

one-time lambda
one-time lambda
one-time lambda
lambda as variable
lambda as variable
lambda as variable
0
mzoz