S-JIS[2014-03-23/2018-10-01] 変更履歴

Java関数型インターフェース

Javaの関数型インターフェースについて。


関数型インターフェース

関数型インターフェースは、ラムダ式メソッド参照の代入先になれるインターフェースのこと。

関数型インターフェースの条件は、大雑把に言って、定義されている抽象メソッドが1つだけあるインターフェース。
staticメソッドデフォルトメソッドは含まれていても構わない(関数型インターフェースの条件としては無視される)。
また、Objectクラスにあるpublicメソッドがインターフェース内に抽象メソッドとして定義されている場合、そのメソッドも無視される。
(この条件を満たすインターフェースを、JDK1.8で「関数型インターフェース」と呼ぶようになった)

// このsubmitメソッドの引数はCallable<Integer>であり、
// Callableインターフェースはcallメソッドだけしか定義されていないので、関数型インターフェースとして扱える。
// したがって、ラムダ式を渡すことが出来る。

	ExecutorService pool = Executors.newSingleThreadExecutor();
	try {
		Future<Integer> future = pool.submit(() -> {
			System.out.println("hoge");
			return 123;
		});
		int result = future.get();
	} finally {
		pool.shutdownNow();
	}
// Collections.sortメソッドの第2引数はComparatorインターフェースであり、
// Comparatorには抽象メソッドはcompareしか定義されていないので、関数型インターフェースとして扱える。
// したがって、ラムダ式を渡すことが出来る。

	List<Integer> list = Arrays.asList(1, 3, 2);
	Collections.sort(list, (o1, o2) -> Integer.compare(o1, o2));
// List#forEachメソッドの引数はConsumerインターフェースであり、
// Consumerインターフェースには抽象メソッドはacceptしか定義されていないので、関数型インターフェースとして扱える。
// したがって、メソッド参照を渡すことが出来る。

	List<Integer> list = Arrays.asList(1, 3, 2);
	list.forEach(System.out::println);

FunctionalInterfaceアノテーション

関数型インターフェースの条件を満たしたインターフェースであれば、自動的に関数型インターフェースとして使用できる。

しかし、インターフェースを関数型インターフェースとして定義したい場合は、java.lang.FunctionalInterfaceアノテーションを付けるのが良い。
FunctionalInterfaceアノテーションを付けていると、関数型インターフェースの条件を満たしていない場合にコンパイルエラーになってくれる。
(また、このアノテーションが付いていれば、関数型インターフェースを目的としていることが分かりやすい)

@FunctionalInterface
public interface FuncInterfaceExample {

	public int example(int n);
}

標準的な関数型インターフェース

ラムダ式メソッド参照を受け取れるようにしたいメソッドは、関数型インターフェースの条件を満たすようなインターフェースを受け取るように作ればよい。

	戻り型 自メソッド名(関数型インターフェース 引数, …) {
		〜
	}

自分が欲しい目的に応じて、インターフェースを作成する。

しかし、個別にインターフェースを作らなくても汎用的に使える関数型インターフェースが用意されている。
場合によってはそれを使うのが良いだろう。

参考: irxgroundさんのJava8 ラムダ式用のクラス

引数と戻り型 関数型インターフェース 備考
() T java.util.function Supplier T get() Supplier(get)系。
(引数なしで)値を返す。
基本的には固定値を返すことに使う。
(いわゆる「遅延評価」用)
() boolean java.util.function BooleanSupplier boolean getAsBoolean()
() int java.util.function IntSupplier int getAsInt()
() long java.util.function LongSupplier long getAsLong()
() double java.util.function DoubleSupplier double getAsDouble()
() void java.lang Runnable void run()  
(T) void java.util.function Consumer void accept(T t) Consumer(accept)系。
処理を行う(値を返さない)。
(int) void java.util.function IntConsumer void accept(int value)
(long) void java.util.function LongConsumer void accept(long value)
(double) void java.util.function DoubleConsumer void accept(double value)
(T, U) void java.util.function BiConsumer void accept(T t, U u)
(T, int) void java.util.function ObjIntConsumer void accept(T t, int value)
(T, long) void java.util.function ObjLongConsumer void accept(T t, long value)
(T, double) void java.util.function ObjDoubleConsumer void accept(T t, double value)
(T) boolean java.util.function Predicate boolean test(T t) Predicate(test)系。
条件判定する(booleanを返す)。
(int) boolean java.util.function IntPredicate boolean test(int value)
(long) boolean java.util.function LongPredicate boolean test(long value)
(double) boolean java.util.function DoublePredicate boolean test(double value)
(T, U) boolean java.util.function BiPredicate boolean test(T t, U u)
(T) R java.util.function Function R apply(T t) Function(apply)系。
引数を受け取り、任意のクラスを返す。
(int) R java.util.function IntFunction R apply(int value)
(long) R java.util.function LongFunction R apply(long value)
(double) R java.util.function DoubleFunction R apply(double value)
(T, U) R java.util.function BiFunction R apply(T t, U u)
(T) int java.util.function ToIntFunction int applyAsInt(T value) ToIntFunction(apply)系。
引数を受け取り、intを返す。
(long) int java.util.function LongToIntFunction int applyAsInt(long value)
(double) int java.util.function DoubleToIntFunction int applyAsInt(double value)
(T, U) int java.util.function ToIntBiFunction int applyAsInt(T t, U u)
(T) long java.util.function ToLongFunction long applyAsLong(T value) ToLongFunction(apply)系。
引数を受け取り、longを返す。
(int) long java.util.function IntToLongFunction long applyAsLong(int value)
(double) long java.util.function DoubleToLongFunction long applyAsLong(double value)
(T, U) long java.util.function ToLongBiFunction long applyAsLong(T t, U u)
(T) double java.util.function ToDoubleFunction double applyAsDouble(T value) ToDoubleFunction(apply)系。
引数を受け取り、doubleを返す。
(int) double java.util.function IntToDoubleFunction double applyAsDouble(int value)
(long) double java.util.function LongToDoubleFunction double applyAsDouble(long value)
(T, U) double java.util.function ToDoubleBiFunction double applyAsDouble(T t, U u)
(T) T java.util.function UnaryOperator T apply(T t) UnaryOperator(apply)系。
引数と同一の型を返す。
(int) int java.util.function IntUnaryOperator int applyAsInt(int operand)
(long) long java.util.function LongUnaryOperator long applyAsLong(long operand)
(double) double java.util.function DoubleUnaryOperator double applyAsDouble(double operand)
(T, T) T java.util.function BinaryOperator T apply(T t, T u) BinaryOperator(apply)系。
引数(2つ)と同一の型を返す。
(int, int) int java.util.function IntBinaryOperator int applyAsInt(int left, int right)
(long, long) long java.util.function LongBinaryOperator long applyAsLong(long left, long right)
(double, double) double java.util.function DoubleBinaryOperator double applyAsDouble(double left, double right)

Supplier

Supplierは値を返す(供給する)為の関数型インターフェース。[2014-04-04]
引数なしで何らかの値を返す。返す値は呼ばれるごとに毎回異なっていても構わない(固定であることは要求されていない)。

@FunctionalInterface
public interface Supplier<T> {

	// () -> T
	public T get();
}

プリミティブ型を扱うSupplierも用意されている。[/2014-04-13]

インターフェース名 関数としての型 説明
Supplier<T> () -> T Tを返す。
BooleanSupplier () -> boolean booleanを返す。
IntSupplier () -> int intを返す。
LongSupplier () -> long longを返す。
DoubleSupplier () -> double doubleを返す。

Supplierの例
メソッド(例) 実行結果(例) 説明 同様のメソッドを持つ
インターフェース
Supplier<String> s = () -> "abc";
System.out.println(s.get());
abc ラムダ式を渡すようにしておくと、
getメソッドが呼ばれたときだけそのラムダ式が呼ばれる。
Loggerでは出力対象ログレベルによっては呼ばれないこともあるので、
重たい処理を行った結果を返す場合に有用。
実際に呼ばれるまで処理が行われないことになるので、
「遅延評価」と呼ばれる。
Supplier
BooleanSupplier
IntSupplier
LongSupplier
DoubleSupplier
get
getAsBoolean
getAsInt
getAsLong
getAsDouble
List<String> list = Arrays.asList("a", "b");
Logger logger = Logger.getLogger("test");
logger.info(() -> "list=" + list);
情報: list=[a, b]

Consumer

Consumerは、引数を受け取り、それを使って処理を行う為の関数型インターフェース。[2014-04-04]
値を返さないので、基本的に副作用を起こす目的で使用する。

@FunctionalInterface
public interface Consumer<T> {

	// (T) -> void
	public void accept(T t);
〜
}

プリミティブ型を扱うConsumerや引数を2つ受け取るConsumerも用意されている。[/2014-04-13]

インターフェース名 関数としての型 説明
Runnable () -> void  
Consumer (T) -> void Tを受け取って処理する関数
IntConsumer (int) -> void intを受け取って処理する関数
LongConsumer (long) -> void longを受け取って処理する関数
DoubleConsumer (double) -> void doubleを受け取って処理する関数
BiConsumer<T, U> (T, U) -> void T,Uを受け取って処理する関数
ObjIntConsumer<T> (T, int) -> void T,intを受け取って処理する関数
ObjLongConsumer<T> (T, long) -> void T,longを受け取って処理する関数
ObjDoubleConsumer<T> (T, double) -> void T,doubleを受け取って処理する関数

Consumerの例
メソッド(例) 実行結果(例) 説明 同様のメソッドを持つ
インターフェース
Consumer<String> c = s -> System.out.println(s);
c.accept("abc");
abc printlnは値をコンソールに出力する(という副作用を起こしている)。 Consumer
IntConsumer
LongConsumer
DoubleConsumer
BiConsumer
ObjIntConsumer
ObjLongConsumer
ObjDoubleConsumer
accept
Consumer<String> c1 = s -> System.out.println("c1=" + s);
Consumer<String> c2 = s -> System.out.println("c2=" + s);
Consumer<String> c = c1.andThen(c2);
c.accept("abc");
c1=abc
c2=abc
andThenメソッドで2つのConsumerをつなぐ。
つないだConsumerのacceptが順番に呼ばれる。
Consumer
IntConsumer
LongConsumer
DoubleConsumer
BiConsumer
andThen

Predicate

Predicateは判定を行う為の関数型インターフェース。[2014-04-04]
引数を受け取り、判定を行い、真偽値(判定結果)を返す。

@FunctionalInterface
public interface Predicate<T> {

	// (T) -> boolean
	public boolean test(T t);
〜
}

プリミティブ型を扱うPredicateや引数を2つ受け取るPredicateも用意されている。[/2014-04-13]

インターフェース名 関数としての型 説明
Predicate<T> (T) -> boolean Tを受け取って判定する関数
IntPredicate (int) -> boolean intを受け取って判定する関数
LongPredicate (long) -> boolean longを受け取って判定する関数
DoublePredicate (double) -> boolean doubleを受け取って判定する関数
BiPredicate<T, U> (T, U) -> boolean T,Uを受け取って判定する関数

Predicateの例
メソッド(例) 実行結果(例) 説明 同様のメソッドを持つ
インターフェース
Predicate<String> p = s -> s.isEmpty();
System.out.println(p.test("abc"));
System.out.println(p.test(""));
false
true
  Predicate
IntPredicate
LongPredicate
DoublePredicate
BiPredicate
test
Predicate<String> isEmpty = s -> s.isEmpty();
Predicate<String> nonEmpty = isEmpty.negate();
System.out.println(nonEmpty.test("abc"));
System.out.println(nonEmpty.test(""));
true
false
negateを使うと、条件判定を反転(否定)した関数を作れる。 Predicate
IntPredicate
LongPredicate
DoublePredicate
BiPredicate
negate
Predicate<String> isEmpty = s -> s.isEmpty();
Predicate<String> nonEmpty = Predicate.not(isEmpty);
System.out.println(nonEmpty.test("abc"));
System.out.println(nonEmpty.test(""));
true
false
Java11でPredicateにnotメソッドが追加された。[2018-10-01]
(JavaのStreamにはScalaのfilterNotのようなメソッドは無いので、
stream.filter(Predicate.not(ラムダ式))のようにして使う)
   
IntPredicate p3 = n -> (n % 3) == 0;
IntPredicate p5 = n -> (n % 5) == 0;
IntPredicate p = p3.and(p5);
System.out.println(p.test(3));
System.out.println(p.test(4));
System.out.println(p.test(5));
System.out.println(p.test(15));
false
false
false
true
2つのPredicateをandメソッドでつなぐと、
双方を満たしたときにtrueを返す関数になる。
これは「&&」と同等であり、最初の条件が満たされなかったらその時点でfalseになる。
Predicate
IntPredicate
LongPredicate
DoublePredicate
BiPredicate
and
IntPredicate p3 = n -> (n % 3) == 0;
IntPredicate p5 = n -> (n % 5) == 0;
IntPredicate p = p3.or(p5);
System.out.println(p.test(3));
System.out.println(p.test(4));
System.out.println(p.test(5));
System.out.println(p.test(15));
true
false
true
true
2つのPredicateをorメソッドでつなぐと、
どちらかを満たしたときtrueを返す関数になる。
これは「||」と同等であり、最初の条件を満たしたらその時点でtrueになる。
Predicate
IntPredicate
LongPredicate
DoublePredicate
BiPredicate
or
Predicate<String> p = Predicate.isEqual("abc");
System.out.println(p.test("abc"));
System.out.println(p.test("def"));
System.out.println(p.test(null));
true
false
false
isEqualメソッドを使うと、あるオブジェクトと等しいかどうかを返す関数を作れる。
isEqualの引数がnull以外だった場合は、オブジェクトのequalsメソッドで比較される。
isEqualの引数がnullだった場合は、testメソッドはnullかどうかを判定する。
   
Predicate<String> p = Predicate.isEqual(null);
System.out.println(p.test("abc"));
System.out.println(p.test("def"));
System.out.println(p.test(null));
false
false
true

Function

Functionは、値を変換する為の関数型インターフェース。[2014-04-04]
引数を受け取り、変換(演算)して別の値を返す。

@FunctionalInterface
public interface Function<T, R> {

	// (T) -> R
	public R apply(T t);	// TをRに変換する
〜
}

プリミティブ型を扱うFunctionや引数を2つ取るFunctionも用意されている。[/2014-04-13]

インターフェース名 関数としての型 説明 備考
Function<T, R> (T) -> R Tを受け取ってRを返す関数 引数の型と戻り型が同一の場合はUnaryOperatorを使う。
IntFunction<R> (int) -> R intを受け取ってRを返す関数 戻り型もintの場合はIntUnaryOperatorを使う。
LongFunction<R> (long) -> R longを受け取ってRを返す関数 戻り型もlongの場合はLongUnaryOperatorを使う。
DoubleFunction<R> (double) -> R doubleを受け取ってRを返す関数 戻り型もdoubleの場合はDoubleUnaryOperatorを使う。
ToIntFunction<T> (T) -> int 引数を受け取ってintを返す関数  
ToLongFunction<T> (T) -> long 引数を受け取ってlongを返す関数  
ToDoubleFunction<T> (T) -> double 引数を受け取ってdoubleを返す関数  
  (int) -> int   intを受け取ってintを返す関数はIntUnaryOperator
IntToLongFunction (int) -> long intを受け取ってlongを返す関数  
IntToDoubleFunction (int) -> double intを受け取ってdoubleを返す関数  
LongToIntFunction (long) -> int longを受け取ってintを返す関数  
  (long) -> long   longを受け取ってlongを返す関数はLongUnaryOperator
LongToDoubleFunction (long) -> double longを受け取ってdoubleを返す関数  
DoubleToIntFunction (double) -> int doubleを受け取ってintを返す関数  
DoubleToLongFunction (double) -> long doubleを受け取ってlongを返す関数  
  (double) -> double   doubleを受け取ってdoubleを返す関数はDoubleUnaryOperator
BiFunction<T, U, R> (T, U) -> R 引数を2つ受け取ってRを返す関数 全ての引数の型と戻り型が同一の場合はBinaryOperator
ToIntBiFunction<T, U> (T, U) -> int 引数を2つ受け取ってintを返す関数 全ての引数の型と戻り型がintの場合はIntBinaryOperator
ToLongBiFunction<T, U> (T, U) -> long 引数を2つ受け取ってlongを返す関数 全ての引数の型と戻り型がlongの場合はLongBinaryOperator
ToDoubleBiFunction<T, U> (T, U) -> double 引数を2つ受け取ってdoubleを返す関数 全ての引数の型と戻り型がdoubleの場合はDoubleBinaryOperator

Functionの例
メソッド(例) 実行結果(例) 説明 同様のメソッドを持つ
インターフェース
Function<String, File> f = s -> new File("/tmp", s);
File file = f.apply("test.txt");
System.out.println(file);
/tmp/test.txt   Function
IntFunction
LongFunction
DoubleFunction
BiFunction
apply
ToIntFunction<String> f = s -> s.length();
int n = f.applyAsInt("abc");
System.out.println(n);
3 ToIntFunction
LongToIntFunction
DoubleToIntFunction
ToIntBiFunction
applyAsInt
IntToLongFunction f = Integer::toUnsignedLong;
long value = f.applyAsLong(-1);
System.out.println(value);
4294967295 ToLongFunction
IntToLongFunction
DoubleToLongFunction
ToLongBiFunction
applyAsLong
IntToDoubleFunction f = n -> Math.pow(2, n);
double value = f.applyAsDouble(3);
System.out.println(value);
8.0 ToDoubleFunction
IntToDoubleFunction
LongToDoubleFunction
ToDoubleBiFunction
applyAsDouble
Function<String, File> f1 = s -> new File(s);
Function<File, List<File>> f2 = dir -> Arrays.asList(dir.listFiles());
Function<String, List<File>> f = f1.andThen(f2);
System.out.println(f.apply("/tmp"));
[/tmp/test.txt] andThenメソッドでFunctionを渡すと、
1つ目のFunctionの結果を2つ目のFunctionに渡す。
Function
BiFunction
andThen
BiFunction<File, String, File> f1 = (f, s) -> new File(f, s);
Function<File, List<String>> after = f -> {
  try {
    return Files.readAllLines(f.toPath());
  } catch (IOException e) {
    throw new UncheckedIOException(e);
  }
};
BiFunction<File, String, List<String>> func = f1.andThen(after);
System.out.println(func.apply(new File("/tmp"), "test.txt"));
 
Function<String, File> f1 = s -> new File(s);
Function<File, List<File>> f2 = dir -> Arrays.asList(dir.listFiles());
Function<String, List<File>> f = f2.compose(f1);
System.out.println(f.apply("/tmp"));
[/tmp/test.txt] composeメソッドで2つのFunctionをつなぐと、
先に2つ目のFunctionを処理し、
その結果を1つ目のFunctionに渡す。
Function compose
Function<String, String> f = Function.identity();
System.out.println(f.apply("abc"));
abc identityメソッドは、「同じ値を返す関数」を返す。
s -> s」と同じ。
Function identity

UnaryOperator

UnaryOperatorは、単項演算子を表す関数型インターフェース。[2014-04-05]
引数を1つ受け取り、演算を行い、引数と同じ型の値を返す。

@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {

	// (T) -> T
//	public T apply(T t);
〜
}

UnaryOperatorは特殊なFunctionである。
FunctionはT型を受け取ってR型(引数と異なる型)を返せるが、UnaryOperatorはT型を受け取ってT型を返すFunctionになっている。

プリミティブ型を扱うUnaryOperatorも用意されている。[/2014-04-13]

インターフェース名 関数としての型 説明 備考
UnaryOperator<T> (T) -> T Tを受け取ってTを返す関数 Function<T, T>である。
IntUnaryOperator (int) -> int intを受け取ってintを返す関数 IntFunctionやToIntFunctionとは継承関係は無い。
LongUnaryOperator (long) -> long longを受け取ってlongを返す関数 LongFunctionやToLongFunctionとは継承関係は無い。
DoubleUnaryOperator (double) -> double doubleを受け取ってdoubleを返す関数 DoubleFunctionやToDoubleFunctionとは継承関係は無い。

UnaryOperatorの例
メソッド(例) 実行結果(例) 説明 同様のメソッドを持つ
インターフェース
UnaryOperator<String> op = s -> s.toUpperCase();
System.out.println(op.apply("abc"));
ABC   UnaryOperator
IntUnaryOperator
LongUnaryOperator
DoubleUnaryOperator
apply
applyAsInt
applyAsLong
applyAsDouble
DoubleUnaryOperator op = n -> n * 1.08;
System.out.println(op.applyAsDouble(500));
540.0
IntUnaryOperator op1 = n -> n * 10;
IntUnaryOperator op2 = n -> n + 1;
IntUnaryOperator op = op1.andThen(op2);
System.out.println(op.applyAsInt(2));
21 andThenメソッドで2つのUnaryOperatorをつなぐと、
1つ目のUnaryOperatorの結果を2つ目のUnaryOperatorに渡す。
UnaryOperator
IntUnaryOperator
LongUnaryOperator
DoubleUnaryOperator
andThen
IntUnaryOperator op1 = n -> n * 10;
IntUnaryOperator op2 = n -> n + 1;
IntUnaryOperator op = op1.compose(op2);
System.out.println(op.applyAsInt(2));
30 composeメソッドで2つのUnaryOperatorをつなぐと、
2つ目のUnaryOperatorを先に処理し、
その結果を1つ目のUnaryOperatorに渡す。
UnaryOperator
IntUnaryOperator
LongUnaryOperator
DoubleUnaryOperator
compose
UnaryOperator<String> op = UnaryOperator.identity();
System.out.println(op.apply("abc"));
abc identityメソッドは、同じ値を返す関数。
s -> s」と同じ。
UnaryOperator
IntUnaryOperator
LongUnaryOperator
DoubleUnaryOperator
identity

BinaryOperator

BinaryOperatorは、二項演算子を表す関数型インターフェース。[2014-04-05]
同じ型の2つの引数を受け取り、演算を行い、引数と同じ型の値を返す。

@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {

	// (T, T) -> T
//	public T apply(T left, T right);
〜
}

BinaryOperatorは特殊なBiFunctionである。
BiFunctionはT型,U型を受け取ってR型(引数と異なる型)を返せるが、BinaryOperatorはT型を2つ受け取ってT型を返すBiFunctionになっている。

プリミティブ型を扱うBinaryOperatorも用意されている。[/2014-04-13]

インターフェース名 関数としての型 説明 備考
BinaryOperator<T> (T, T) -> T Tを2つ受け取ってTを返す関数 BiFunction<T, T, T>である。
IntBinaryOperator (int, int) -> int intを2つ受け取ってintを返す関数 ToIntBiFunctionとは継承関係は無い。
LongBinaryOperator (long, long) -> long longを2つ受け取ってlongを返す関数 ToLongBiFunctionとは継承関係は無い。
DoubleBinaryOperator (double, double) -> double doubleを2つ受け取ってdoubleを返す関数 ToDoubleBiFunctionとは継承関係は無い。

BinaryOperatorの例
メソッド(例) 実行結果(例) 説明 同様のメソッドを持つ
インターフェース
BinaryOperator<String> op = (s1, s2) -> s1 + s2;
System.out.println(op.apply("abc", "def"));
abcdef   BinaryOperator
IntBinaryOperator
LongBinaryOperator
DoubleBinaryOperator
apply
applyAsInt
applyAsLong
applyAsDouble
IntBinaryOperator op = Integer::sum;
System.out.println(op.applyAsInt(123, 456));
579
LongBinaryOperator op = Long::max;
System.out.println(op.applyAsLong(123L, 456L));
456
BinaryOperator<String> op = BinaryOperator.minBy((s1, s2) -> s1.compareTo(s2));
String s = op.apply("abc", "zzz");
abc 与えられたComparatorを使用して、小さい方を返す。 BinaryOperator minBy
BinaryOperator<String> op = BinaryOperator.maxBy((s1, s2) -> s1.compareTo(s2));
String s = op.apply("abc", "zzz");
zzz 与えられたComparatorを使用して、大きい方を返す。 BinaryOperator maxBy

JDK1.8でIntegerやLongクラスにsum(a, b)max(a, b)min(a, b)というstaticメソッドが追加されたが、IntBinaryOperator等にメソッド参照として渡すことを想定しているのではないかと思う。

参考: opengl-8080さんのjava.util.function以下の関数インターフェース使い方メモ


Java目次へ戻る / 新機能へ戻る / 技術メモへ戻る
メールの送信先:ひしだま