0Pricing
Java Academy · レッスン

カスタム関数型インターフェース

@FunctionalInterface で独自に定義します。

「カスタム関数型インターフェース」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。

インターフェースが関数型になる条件

関数型インターフェースには、抽象メソッド(SAM)がちょうど1つあります。このメソッド1つをラムダが実装します。

  • defaultメソッドとstaticメソッドは、この数に含まれません。
interface Greeter {
    String greet(String name);
}

public class Main {
    public static void main(String[] args) {
        Greeter g = name -> "Hello, " + name;
        System.out.println(g.greet("Ada"));
    }
}

@FunctionalInterfaceアノテーション

@FunctionalInterfaceは、抽象メソッドを1つだけにする規則をコンパイラーに適用させます。誤って2つ目の抽象メソッドを追加すると、コンパイルに失敗します。

@FunctionalInterface
interface Calculator {
    int compute(int a, int b);
}

public class Main {
    public static void main(String[] args) {
        Calculator add = (a, b) -> a + b;
        System.out.println(add.compute(3, 4));
    }
}

アノテーションが役立つ理由

このアノテーションは意図を記録し、契約を保護します。アノテーションがなくてもインターフェースは関数型として機能しますが、チームメンバーが別の抽象メソッドを追加して壊してしまう可能性があります。

@FunctionalInterface
interface Transformer {
    String apply(String input);
}

public class Main {
    public static void main(String[] args) {
        Transformer reverse = s -> new StringBuilder(s).reverse().toString();
        System.out.println(reverse.apply("abc"));
    }
}

ジェネリクスを使ったカスタムインターフェース

組み込みのインターフェースと同じように、インターフェースをジェネリックにすることもできます。

@FunctionalInterface
interface Converter<S, T> {
    T convert(S source);
}

public class Main {
    public static void main(String[] args) {
        Converter<String, Integer> toInt = Integer::parseInt;
        System.out.println(toInt.convert("123") + 1);
    }
}

defaultメソッドは使用可能

関数型インターフェースにはdefaultメソッドを定義できます。これにより、抽象メソッドを1つだけにする規則を壊さずに動作を追加できます。

@FunctionalInterface
interface Operation {
    int apply(int x);

    default Operation then(Operation next) {
        return x -> next.apply(this.apply(x));
    }
}

public class Main {
    public static void main(String[] args) {
        Operation inc = x -> x + 1;
        Operation dbl = x -> x * 2;
        System.out.println(inc.then(dbl).apply(5));
    }
}

staticメソッドも使用可能

staticメソッドは、Function.identity()と同じようにファクトリとして機能します。

@FunctionalInterface
interface Mapper {
    int map(int x);

    static Mapper identity() {
        return x -> x;
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println(Mapper.identity().map(99));
    }
}

チェック例外の送出

抽象メソッドがチェック例外を宣言している場合、ラムダからその例外を送出できます。組み込みのFunctionではこれができないため、独自のインターフェースを定義する理由の1つになります。

@FunctionalInterface
interface RiskyParser {
    int parse(String s) throws NumberFormatException;
}

public class Main {
    public static void main(String[] args) {
        RiskyParser p = Integer::parseInt;
        System.out.println(p.parse("77"));
    }
}

カスタムインターフェースをメソッドに渡す

独自のインターフェースをパラメーター型として使うことで、呼び出し側がラムダで動作を提供できるようになります。

@FunctionalInterface
interface IntCombiner {
    int combine(int a, int b);
}

public class Main {
    static int reduce(int[] arr, IntCombiner c) {
        int acc = arr[0];
        for (int i = 1; i < arr.length; i++) acc = c.combine(acc, arr[i]);
        return acc;
    }

    public static void main(String[] args) {
        int[] data = {1, 2, 3, 4};
        System.out.println(reduce(data, (a, b) -> a + b));
    }
}

3引数のインターフェース

JDKに用意されているのは、1引数および2引数のインターフェースだけです。3つ以上の引数を扱う場合は、独自のインターフェースを定義してください。

@FunctionalInterface
interface TriFunction<A, B, C, R> {
    R apply(A a, B b, C c);
}

public class Main {
    public static void main(String[] args) {
        TriFunction<Integer, Integer, Integer, Integer> sum3 = (a, b, c) -> a + b + c;
        System.out.println(sum3.apply(1, 2, 3));
    }
}

メソッド参照はカスタムインターフェースに適合する

SAMとシグネチャが一致するメソッドは、メソッド参照として渡せます。

@FunctionalInterface
interface StringTest {
    boolean check(String s);
}

public class Main {
    public static void main(String[] args) {
        StringTest blank = String::isBlank;
        System.out.println(blank.check("   "));
    }
}

匿名クラスとラムダ

ラムダは、関数型インターフェースを実装する匿名クラスを簡潔に置き換えるものです。どちらも同じ動作を実現します。

@FunctionalInterface
interface Action {
    void run();
}

public class Main {
    public static void main(String[] args) {
        Action old = new Action() {
            public void run() { System.out.println("anonymous"); }
        };
        Action neo = () -> System.out.println("lambda");
        old.run();
        neo.run();
    }
}

理解度チェック

@FunctionalInterfaceは何を保証しますか。

まとめ

独自の関数型インターフェースを定義しました。

  • 関数型インターフェースには抽象メソッド(SAM)がちょうど1つあります。
  • @FunctionalInterfaceはコンパイル時にこの規則を適用します。
  • defaultメソッドとstaticメソッド、ジェネリクス、チェック例外はすべて使用できます。
  • カスタムインターフェースは、3引数の関数やチェック例外を扱うラムダなど、JDKでカバーされていない用途を補います。

よくある質問

「カスタム関数型インターフェース」レッスンは無料ですか?

はい。「カスタム関数型インターフェース」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。

「カスタム関数型インターフェース」で何を学びますか?

@FunctionalInterface で独自に定義します。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Java Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「カスタム関数型インターフェース」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このJava Academyレッスンでコードを書いて実行できますか?

はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. Function と BiFunction
  2. Supplier と Consumer
  3. Predicate と合成
  4. カスタム関数型インターフェース
← Java Academyに戻る