関数型インターフェース
関数型インターフェースとは、抽象メソッドを一つだけ持つインターフェースのことです(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パッケージが導入されました。このパッケージには、様々な状況で使用できる一連のインターフェースが含まれています。
標準ライブラリの関数型インターフェース
命名規則
- 基本型ではなくオブジェクトのみを扱う場合、名前は
Function、Consumer、Predicateなどになります。パラメータ型はジェネリクスで追加します。 - パラメータが基本型の場合、名前の最初の部分で示されます。例えば
LongConsumer、DoubleFunction、IntPredicateなどですが、基本型を返す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());
}
}