Java 8で使用される機能インターフェースは何ですか?


154

私はJava 8で「機能的インターフェース」という新しい用語に出会いました。ラムダ式で作業しているときに、その使用法を1つしか見つけることができませんでした。

Java 8にはいくつかの組み込みの関数型インターフェースが用意されており、関数型インターフェースを定義したい場合は、@FunctionalInterfaceアノテーションを利用できます。これにより、インターフェイスで単一のメソッドのみを宣言できます。

例えば:

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

ラムダ式を操作するだけでなく、Java 8でどれほど便利ですか?

ここでの質問は、私が尋ねたものとは異なります。ラムダ式を操作するときに関数型インターフェースが必要な理由を尋ねています。私の質問は、ラムダ式以外に関数型インターフェースに他の用途がある理由は何ですか。)


1
このリンクは重複しているようです。また、Functional Interfaceにメソッドが1つしかない理由についても説明します。stackoverflow.com/questions/33010594/...
Kulbhushanシン

1
@KulbhushanSingh投稿する前にこの質問を見ました...どちらの質問も違いを感じます...
Madhusudan

回答:


127

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

ただし、このアノテーションなしでラムダを使用でき、アノテーションなしでメソッドをオーバーライドできます@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は機能的なインターフェースではありません


43
より正確に言うと、関数型インターフェイスのメソッドをオーバーライドしない抽象メソッドを1つだけ持つ必要がありjava.lang.Objectます。
Holger

9
...そしてそれを「つ以上持っていないために、わずかに違うpublicほかの方法をstaticしてdefault...」
ホルガー

4
まだそれを持っているどんなポイントも理解していません。地球上の誰もが、彼/彼女のインターフェースが持っているメソッドの数をチェックするのを煩わせるのはなぜですか。マーカーインターフェイスには、まだポイントと特定の目的があります。ドキュメンテーションと回答はそれが何をするかを説明するだけであり、それがどのように使用されるかについては説明していません。そして、「使用」はまさにOPが求めたものです。だから私はこの答えはお勧めしません。
saran3h

1
@VNTコンパイルエラーはこのインターフェイスのクライアントを取得しますが、インターフェイス自体は変更できません。このアノテーションを使用すると、コンパイルエラーがインターフェイスにあるため、インターフェイスのクライアントを破壊する人がいないことを確認します。
Sergii Bishyr

2
これはそれらの使い方を示していますが、なぜ必要なのかは説明していません。
シェイク

14

ドキュメントには、確かに目的の違いを作ります

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

とユースケース

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

その表現は、一般的に他のユースケースを排除しません。主な目的は機能インターフェース実際の質問はラムダ式とメソッド/コンストラクター参照以外の機能インターフェース使用例はありますか?」に要約されます。

関数型インターフェースは、Java言語仕様で定義されたJava言語構造であるため、その質問に答えられるのはその仕様のみです。

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

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

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

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


ちょうど(あなたが答えにないことを選択することができる)が多すぎるか、無関係な少しを尋ねるかもしれませんが、誰かがユーティリティ作成したときに何を示唆しているpublic static String generateTaskId()他の誰かのようにそれを書くことを選んだ、それはより多くの「機能」を作るために対をpublic class TaskIdSupplier implements Supplier<String>持つget用いる方法既存の世代の実装。これは機能的なインターフェースの誤用ですか、特にSupplierJDK組み込みのを再利用していますか?PS:私はこれを尋ねるよりよい場所/ Q&Aを見つけることができませんでした。提案できる場合は移行してください。
ナマン

1
@Naman名前付きクラスを作成するときに、ユーティリティメソッドをより機能的にしていませんTaskIdSupplier。さて、問題は名前付きクラスを作成した理由です。このような名前付きの型が必要となるシナリオがあります。たとえば、による実装の検索をサポートする場合などですServiceLoader。それを実装させることに何の問題もありませんSupplier。ただし、不要な場合は作成しないでください。だけが必要な場合はSupplier<String>、使用するだけで十分でDeclaringClass::generateTaskIdあり、明示的なクラスの必要性を排除することがこの言語機能のポイントです。
ホルガー

正直に言って、私が伝えていた推奨の正当化に向けて探していました。何らかの理由で、私はTaskIdSupplier実装が努力の価値があるとは本当に感じていませんでしたが、そのときの概念はServiceLoader完全に私の頭から離れました。これらのディスカッション中に、私たちが持っていたいくつかの質問に遭遇しました。たとえば、先に進んで独自のインターフェイスを開発できる場合のの存在は何ですか?Supplierpublicそしてなぜpublic static Supplier<String> TASK_ID_SUPPLIER = () ->...グローバル定数として持っていないのですか?。(1/2)
ナマン

1
@Namanは、Javaで関数を表現する慣用的な方法がメソッドであり、それらの関数の評価は、それらを呼び出すことと同じです。開発者にのvariable.genericMethodName(args)代わりに強制するべきではありませんmeaningfulMethodName(args)。クラス型を使用して関数を表すことは、ラムダ式/メソッド参照または手動で作成されたクラスのいずれであっても、関数を渡す手段すぎません(Javaに真の関数型がない場合)。これは、必要な場合にのみ行う必要があります。
Holger

1
小さなコードフラグメントのみが渡される場合、それをカプセル化するラムダ式を作成できます。メソッドのように呼び出す必要がある場合(コードフラグメントが自明ではない場合に、テストが必要なシナリオが含まれる場合)は、呼び出すことができる名前付きメソッドを作成し、メソッド参照またはラムダ式/明示的なクラスを使用します。呼び出しをカプセル化し、必要なときに受け渡します。定数は、コードに埋め込まれたラムダ式またはメソッド参照の効率を信頼できない場合にのみ役立ちます。つまり、定数はほとんど必要ありません。
Holger

12

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


10

どういたしまして。ラムダ式は、その注釈の唯一のポイントです。


6
まあ、lamdbasは注釈なしでも動作します。これ@Overrideは、「機能的」なものを書くつもりであることをコンパイラーに知らせる(そして、スリップした場合はエラーが発生する)ようにアサーションです。
Thilo

1
端的に言って正解ですが、少し短いです。私は時間をかけて、より多くの言葉で同じことを言っているより精巧な回答を追加しました…
Holger

5

ラムダ式は、関数型インターフェイスタイプに割り当てることができますが、メソッド参照や匿名クラスにも割り当てることができます。

Oneの特定の機能のインターフェイスのいいところはjava.util.function、彼らが新しい機能を作成するために構成することができるということである(のようにFunction.andThenしてFunction.composePredicate.andそれらに含まれる便利なデフォルトの方法に起因する、など)。


このコメントについて詳しく説明する必要があります。メソッド参照と新しい関数はどうですか?
K.Nicholas 2018年

5

抽象メソッドを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;
        }
    }

3

機能インターフェース:

  • 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 <>。

機能インターフェースのアプリケーション:

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

関数インターフェイスを学習するには、インターフェイスの最初のデフォルトメソッドを学習し、関数インターフェイスを学習すると、メソッド参照とラムダ式を理解しやすくなります


最初の2つの例には、「抽象的な」キーワードが必要ですか?
sofs1 2018

1
@ sofs1インターフェイスで宣言されたメソッドは、デフォルトでパブリックと抽象の両方です。抽象クラスのメソッドの場合、抽象キーワードを使用する必要があります。ただし、インターフェイスのメソッドにも抽象キーワードを使用することは問題ありません。古いJavaバージョンの互換性のために許可されていますが、お勧めしません。
Ketan

2

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 LambdaFunctionalInterfacesの詳細については


1

@FunctionalInterface これは、Java 8でリリースされた新しいアノテーションで、ラムダ式のターゲットタイプを提供し、コードのコンパイル時チェックで使用されます。

使いたい時:

1-インターフェースに複数の抽象メソッドを含めることはできません。そうしないと、コンパイルエラーが発生します。

1- インターフェースは純粋でなければなりません。つまり、機能インターフェースはステートレスクラスによって実装されることを意図しています。純粋なComparatorインターフェースの例は、実装者の状態に依存しないためです。この場合、コンパイルエラーは発生しませんが、多くの場合、この種のインターフェースでラムダを使用することはできません

java.util.functionパッケージは、以下のような様々な汎用機能インタフェースが含まれているPredicateConsumerFunction、およびSupplier

また、このアノテーションがなくてもラムダを使用できることに注意してください。


1

他の回答と並んで、「ラムダ式で直接使用する以外に関数型インターフェースを使用する理由」の主な理由は、オブジェクト指向であるJava言語の性質に関連している可能性があると思います。

ラムダ式の主な属性は次のとおりです。1。約2を渡すことができ、将来、特定の時間(数回)に実行できます。この機能を言語でサポートするために、他のいくつかの言語はこの問題を単純に扱います。

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

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

またはES6を介して、矢印機能を使用できます。

const myFunction = ... => ...

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

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

ActionListener listener = event -> ...;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.