web-dev-qa-db-ja.com

Java 8での関数型インタフェースの使い方は何ですか?

私はJava 8でFunctional Interfaceという新しい用語に出会いました。

ラムダ式を使って作業している間、私はこのインターフェースの使用法を1つしか見つけることができませんでした。

Java 8はいくつかの組み込みの機能的なインターフェースを提供します、そしてもし我々が機能的なインターフェースを定義したいならば、私たちは@FunctionalInterfaceアノテーションを利用することができます。それは私達がインターフェイスの単一のメソッドだけを宣言することを可能にするでしょう。

例えば:

@FunctionalInterface
interface MathOperation {
    int operation(int a, int b);
}

Java 8では、ラムダ式を扱うだけでなく、どれだけ便利ですか。

ここでの質問は、私が尋ねたものとは異なります。 Lambda式を使って作業しているのに、なぜ関数型インタフェースが必要なのかを尋ねています。私の質問は、なぜラムダ式と直接ではなく関数型インタフェースを使うのかということです。

132
Madhusudan

@FunctionalInterfaceアノテーションは、コードのコンパイル時チェックに役立ちます。 @FunctionalInterface内のstatic内のメソッドをオーバーライドするdefaultObject、およびabstractメソッド以外の複数のメソッド、または機能インターフェースとして使用される他のインターフェースを持つことはできません。

しかし、@Overrideアノテーションなしでメソッドをオーバーライドできるのと同様に、このアノテーションなしでラムダを使うことができます。

ドキュメントから

機能インターフェースは、厳密に1つの抽象メソッドを持ちます。デフォルトのメソッドは実装されているので抽象的ではありません。インタフェースが、Java.lang.Objectのパブリックメソッドの1つをオーバーライドする抽象メソッドを宣言している場合、インタフェースの実装にはJava.lang.Objectまたは他の場所からの実装があるため、それもインタフェースの抽象メソッド数にカウントされません。

これはラムダ式で使用できます

public interface Foo {
  public void doSomething();
}

これはラムダ式では使用できません

public interface Foo {
  public void doSomething();
  public void doSomethingElse();
}

しかしこれはコンパイルエラーを与えるでしょう:

@FunctionalInterface
public interface Foo {
  public void doSomething();
  public void doSomethingElse();
}

'@FunctionalInterface'アノテーションが無効です。 Fooは機能的なインターフェースではありません

115
Sergii Bishyr

ドキュメント は、実際に目的と

インターフェース型宣言がJava言語仕様で定義されている機能インターフェースであることを示すために使用される有益な注釈型。

そしてユースケース

関数型インターフェースのインスタンスは、ラムダ式、メソッド参照、またはコンストラクター参照を使用して作成できることに注意してください。

その文言は一般に他のユースケースを妨げません。主な目的は機能的なインターフェイスを示すことなので、実際の質問はに要約されます。 for機能インターフェースラムダ式とメソッド/コンストラクター参照以外?」

Functional InterfaceはJava言語仕様で定義されたJava言語構成であるため、その仕様のみが回答できます。その質問:

JLS§9.8。機能的インターフェース

クラスを宣言してインスタンス化することでインターフェイスインスタンスを作成する通常のプロセス(§15.9)に加えて、機能インターフェイスのインスタンスをメソッド参照式とラムダ式(§15.13、§15.27)で作成できます。

そのため、Java言語仕様には特に明記されていません。そのセクションで言及されている唯一のユースケースは、メソッド参照式とラムダ式を使用してインターフェイスインスタンスを作成することです。 (これには、仕様でメソッド参照式の1つの形式として記載されているコンストラクタ参照が含まれます)。

したがって、ある文では、いや、Java 8には他のユースケースはありません。

12
Holger

どういたしまして。ラムダ式はそのアノテーションの唯一のポイントです。

10
Louis Wasserman

他の人が言っているように、機能的インターフェースは一つのメソッドを公開するインターフェースです。複数のメソッドがある場合もありますが、他のすべてのメソッドにはデフォルトの実装が必要です。それが「機能的インターフェース」と呼ばれる理由は、それが事実上機能として機能するからです。あなたはパラメータとしてインタフェースを渡すことができるので、それは関数が関数型プログラミング言語のように「第一級の市民」であることを意味します。これには多くの利点があり、Stream APIを使用すると非常に多くの利点があります。もちろん、ラムダ式はそれらの主な明白な用途です。

8
Sina Madani

ラムダ式は関数型のインタフェース型に代入できますが、メソッド参照や匿名クラスにも代入できます。

Java.util.functionの特定の機能的なインターフェースについて素晴らしいことは、それらが含んでいる便利なデフォルトのメソッドのためにそれらが新しい機能(Function.andThenFunction.composePredicate.andなど)を作成するために組み立てられることができるということです。

5
Hank D

抽象メソッドが1つだけのインターフェースは、機能インターフェースと呼ばれます。 @FunctionalInterfaceを使用することは必須ではありませんが、余分なメソッドが誤って追加されないようにするために、機能的なインターフェイスと共に使用することをお勧めします。インターフェースに@FunctionalInterfaceアノテーションが付けられていて、複数の抽象メソッドを使用しようとすると、コンパイラエラーが発生します。

package com.akhi;
    @FunctionalInterface
    public interface FucnctionalDemo {

      void letsDoSomething();
      //void letsGo();      //invalid because another abstract method does not allow
      public String toString();    // valid because toString from Object 
      public boolean equals(Object o); //valid

      public static int sum(int a,int b)   // valid because method static
        {   
            return a+b;
        }
        public default int sub(int a,int b)   //valid because method default
        {
            return a-b;
        }
    }
4
Akhilesh

あなたはJava 8でラムダを使用することができます

  public static void main(String[] args) {
    tentimes(inputPrm -> System.out.println(inputPrm));
    //tentimes(System.out::println);  // You can also replace lambda with static method reference
    }

    public static void tentimes(Consumer myFunction){
      for(int i = 0; i < 10; i++)
        myFunction.accept("hello");
    }

Java Lambdas および FunctionalInterfaces に関する詳細情報

2
Websterix

@FunctionalInterfaceは、Java 8でリリースされた新しい注釈であり、ラムダ式のターゲット型を提供します。これは、コードのコンパイル時チェックに使用されます。

あなたがそれを使いたい時は:

1-あなたのインターフェースは、が複数の抽象メソッドを持っていてはいけません。そうでなければ、コンパイルエラーが発生します。

1-あなたのインターフェース純粋でなければならない、これは機能的インターフェースがステートレスクラスによってインプリメントされることを意味する、純粋の例はインプリメンターステートに依存しないのでComparatorname__インターフェースコンパイルエラーは表示されませんが、多くの場合、この種のインタフェースではラムダを使用できません。

Java.util.functionパッケージには、Predicatename__、Consumername__、Functionname__、およびSuppliername__などのさまざまな汎用機能インターフェースが含まれています。

この注釈なしでラムダを使うことができることにも注意してください。

2
Ahmad Al-Kurdi

他の答えのほかに、「なぜラムダ式ではなく関数型インタフェースを使用するのか」の主な理由は、オブジェクト指向であるJava言語の性質に関連すると考えることができます。

Lambda式の主な属性は次のとおりです。1.これらは渡されることができます2.そして将来的には特定の時間(数回)で実行されることができます。この機能を言語でサポートするために、他のいくつかの言語では単にこの問題に対処しています。

たとえば、Javaスクリプトでは、関数(無名関数、または関数リテラル)をオブジェクトとしてアドレス指定できます。それで、あなたはそれらを簡単に作成することができます、そしてまたそれらは変数などに割り当てられることができます。例えば:

var myFunction = function (...) {
    ...;
}
alert(myFunction(...));

またはES6経由では、矢印機能を使用できます。

const myFunction = ... => ...

これまで、Java言語の設計者は、これらの方法(関数型プログラミング手法)による言及された機能の処理を受け入れていません。彼らは、Java言語はオブジェクト指向であると信じているため、オブジェクト指向の手法でこの問題を解決する必要があります。彼らは単純さとJava言語の一貫性を見逃したくありません。

したがって、それらはインターフェイスを使用します。ただ1つのメソッド(つまり、機能的インターフェイス)を持つインターフェイスのオブジェクトが必要なときは、それをラムダ式で置き換えることができます。といった:

ActionListener listener = event -> ...;
1
MMKarami

機能インターフェース:

  • Java 8で導入された
  • 「単一の抽象」メソッドを含むインタフェース。

例1

   interface CalcArea {   // --functional interface
        double calcArea(double rad);
    }           

例2

interface CalcGeometry { // --functional interface
    double calcArea(double rad);
    default double calcPeri(double rad) {
        return 0.0;
    }
}       

例3

interface CalcGeometry {  // -- not functional interface
    double calcArea(double rad);
    double calcPeri(double rad);
}   

Java8アノテーション - @FunctionalInterface

  • そのインタフェースが1つの抽象メソッドのみを含むことをアノテーションチェックします。そうでなければ、エラーを発生させます。
  • @FunctionalInterfaceがないにもかかわらず、それはまだ機能的なインターフェースです(単一の抽象メソッドがある場合)。注釈は間違いを避けるのに役立ちます。
  • 機能インターフェースは、追加の静的メソッドとデフォルトメソッドを持つことができます。
  • 例えばIterable <>、Comparable <>、Comparator <>。

機能的インタフェースの応用:

  • メソッド参照
  • ラムダ式
  • コンストラクタの参照

機能的インターフェースを学ぶためには、インターフェース内の最初のデフォルトのメソッドを学び、そして機能的インターフェースを学んだ後に、メソッド参照とラムダ式を理解することは簡単になるでしょう。

1
Ketan