Javaの関数型インターフェース:ラムダ式とメソッド参照の基礎

関数型インターフェース

関数型インターフェースとは、抽象メソッドを一つだけ持つインターフェースのことです(Java 8ではデフォルトメソッドも使用できます)。

関数型インターフェースの主な用途は、ラムダ式とメソッド参照を受け取り、そのターゲット要素を定義することです。

基本的な関数型インターフェースの例

// 関数型インターフェース(抽象メソッド一つ)
interface Processor {
    void execute();
}

/* これは以下と同等です
@FunctionalInterface
interface Processor {
    // このメソッドシグネチャはラムダ式のシグネチャを記述します
    // そのため関数記述子とも呼ばれます
    void execute();
}
*/

class ProcessorImpl {
    void execute() {
        System.out.println("メソッド参照による実行");
    }
}

public class FunctionalDemo {
    public static void main(String[] args) {
        // ラムダ式
        // この式のシグネチャは () -> void
        Processor p1 = () -> System.out.println("ラムダ式による実行");
        p1.execute();
        
        // メソッド参照
        Processor p2 = new ProcessorImpl()::execute;
        p2.execute();
    }
}

ラムダ式やメソッド参照を使用する際には、それらを受け取るためのインターフェース(上記のProcessorインターフェース)を定義する必要があります。

毎回必要なインターフェースを作成するのを避けるため、Java 8ではjava.util.functionパッケージが導入されました。このパッケージには、様々な状況で使用できる一連のインターフェースが含まれています。

標準ライブラリの関数型インターフェース

命名規則

  • 基本型ではなくオブジェクトのみを扱う場合、名前はFunctionConsumerPredicateなどになります。パラメータ型はジェネリクスで追加します。
  • パラメータが基本型の場合、名前の最初の部分で示されます。例えばLongConsumerDoubleFunctionIntPredicateなどですが、基本型を返すSupplierインターフェースは例外です。
  • 戻り値が基本型の場合、Toで示します。例えばToLongFunction<T>IntToLongFunctionなど。
  • 戻り値の型がパラメータの型と同じ場合、Operatorになります:単一パラメータの場合はUnaryOperator、2つのパラメータの場合はBinaryOperator
  • パラメータを受け取りブール値を返す場合、**述語(Predicate)**です。
  • 受け取る2つのパラメータの型が異なる場合、名前にBiが含まれます。

コード例

ラムダ式の使用例

class DataPoint{}

class Result{
    DataPoint dp;
    Result(DataPoint dp){
        this.dp = dp;
    }
}

class IntResult{
    int value;
    IntResult(int value){
        this.value = value;
    }
}

class LongResult{
    long value;
    LongResult(long value){
        this.value = value;
    }
}

class DoubleResult{
    double value;
    DoubleResult(double value){
        this.value = value;
    }
}

public class FunctionalExamples {
    static Function transformer1 = dp -> new Result(dp);
    static IntFunction<IntResult> transformer2 = val -> new IntResult(val);
    static LongFunction<LongResult> transformer3 = val -> new LongResult(val);
    static DoubleFunction<DoubleResult> transformer4 = val -> new DoubleResult(val);
    static ToIntFunction<IntResult> extractor1 = ir -> ir.value;
    static ToLongFunction<LongResult> extractor2 = lr -> lr.value;
    static ToDoubleFunction<DoubleResult> extractor3 = dr -> dr.value;
    static IntToLongFunction converter1 = val -> val;
    static IntToDoubleFunction converter2 = val -> val;
    static LongToIntFunction converter3 = val -> (int)val;
    static LongToDoubleFunction converter4 = val -> val;
    static DoubleToIntFunction converter5 = val -> (int)val;
    static DoubleToLongFunction converter6 = val -> (long)val;

    public static void main(String[] args) {
        Result res1 = transformer1.apply(new DataPoint());
        IntResult intRes = transformer2.apply(22);
        LongResult longRes = transformer3.apply(33L);
        DoubleResult doubleRes = transformer4.apply(44.0);
        
        int intValue = extractor1.applyAsInt(intRes);
        long longValue = extractor2.applyAsLong(longRes);
        double doubleValue = extractor3.applyAsDouble(doubleRes);
        
        longValue = converter1.applyAsLong(55);
        doubleValue = converter2.applyAsDouble(66);
        intValue = converter3.applyAsInt(77L);
        doubleValue = converter4.applyAsDouble(88L);
        intValue = converter5.applyAsInt(99.0);
        longValue = converter6.applyAsLong(100.0);
    }
}

メソッド参照の使用例

class InputData{}

class OutputData{}

class ProcessResult{}

public class MethodReferenceDemo {
    static InputData createData() {
        return new InputData();
    }
    
    static int compareData(InputData d1, InputData d2) {
        return 0;
    }
    
    static void processData(InputData data) {
        // データ処理ロジック
    }
    
    static void processTwoData(InputData d1, InputData d2) {
        // 2つのデータを処理するロジック
    }
    
    static ProcessResult transformData(InputData data) {
        return new ProcessResult();
    }
    
    static ProcessResult combineData(InputData d1, InputData d2) {
        return new ProcessResult();
    }
    
    static boolean validateData(InputData data) {
        return true;
    }
    
    static boolean compareTwoData(InputData d1, InputData d2) {
        return true;
    }
    
    static InputData duplicateData(InputData data) {
        return new InputData();
    }
    
    static InputData mergeData(InputData d1, InputData d2) {
        return new InputData();
    }

    public static void main(String[] args) {
        Supplier<InputData> dataSupplier = MethodReferenceDemo::createData;
        InputData data = dataSupplier.get();
        
        Comparator<InputData> dataComparator = MethodReferenceDemo::compareData;
        dataComparator.compare(new InputData(), new InputData());
        
        Consumer<InputData> dataConsumer = MethodReferenceDemo::processData;
        dataConsumer.accept(new InputData());
        
        BiConsumer biDataConsumer = MethodReferenceDemo::processTwoData;
        biDataConsumer.accept(new InputData(), new OutputData());
        
        Function dataTransformer = MethodReferenceDemo::transformData;
        ProcessResult result = dataTransformer.apply(new InputData());
        
        BiFunction biDataTransformer = MethodReferenceDemo::combineData;
        result = biDataTransformer.apply(new InputData(), new OutputData());
        
        Predicate<InputData> dataValidator = MethodReferenceDemo::validateData;
        boolean isValid = dataValidator.test(new InputData());
        
        BiPredicate biDataValidator = MethodReferenceDemo::compareTwoData;
        isValid = biDataValidator.test(new InputData(), new OutputData());
        
        UnaryOperator<InputData> dataDuplicator = MethodReferenceDemo::duplicateData;
        InputData duplicatedData = dataDuplicator.apply(new InputData());
        
        BinaryOperator<InputData> dataMerger = MethodReferenceDemo::mergeData;
        InputData mergedData = dataMerger.apply(new InputData(), new InputData());
    }
}

タグ: Java 関数型インターフェース ラムダ式 メソッド参照

5月21日 00:43 投稿