Java8でメソッド参照をFunction
オブジェクトとして使用してそのメソッドを使用する方法はありますか。
Stream.of("ciao", "hola", "hello")
.map(String::length.andThen(n -> n * 2))
この質問はStream
とは関係ありません。例として使用されているだけです。メソッドのリファレンスについてお答えしたいと思います
これを行う静的メソッドを作成できます。
import Java.util.function.*;
class Test {
public static void main(String[] args) {
Function<String, Integer> function = combine(String::length, n -> n * 2);
System.out.println(function.apply("foo"));
}
public static <T1, T2, T3> Function<T1, T3> combine(
Function<T1, T2> first,
Function<T2, T3> second) {
return first.andThen(second);
}
}
次に、それをユーティリティクラスに入れて、静的にインポートします。
または、コンパイラがあなたが何をしているかを知るために、justが指定された関数を返す、より単純な静的メソッドを作成します。
import Java.util.function.*;
class Test {
public static void main(String[] args) {
Function<String, Integer> function = asFunction(String::length).andThen(n -> n * 2);
System.out.println(function.apply("foo"));
}
public static <T1, T2> Function<T1, T2> asFunction(Function<T1, T2> function) {
return function;
}
}
変数に保存するだけです。
Function<String, Integer> toLength = String::length;
Stream.of("ciao", "hola", "hello")
.map(toLength.andThen(n -> n * 2));
またはキャストを使用できますが、読みにくくなりますIMO:
Stream.of("ciao", "hola", "hello")
.map(((Function<String, Integer>) String::length).andThen(n -> n * 2));
キャストを使用して、インラインで必要なことを実現できるはずです。
Stream.of("ciao", "hola", "hello")
.map(((Function<String, Integer>) String::length).andThen(n -> n * 2))
コンパイラーには「型のヒント」しかないため、実際にオブジェクトを「キャスト」することはなく、実際のキャストのオーバーヘッドはありません。
または、読みやすくするためにローカル変数を使用できます。
Function<String, Integer> fun = String::length
Stream.of("ciao", "hola", "hello")
.map(fun.andThen(n -> n * 2));
より簡潔な3番目の方法は、ユーティリティメソッドを使用することです。
public static <T, X, U> Function<T, U> chain(Function<T, X> fun1, Function<X, U> fun2)
{
return fun1.andThen(fun2);
}
Stream.of("ciao", "hola", "hello")
.map(chain(String::length, n -> n * 2));
これはテストされていないため、この場合、型推論が正しく機能するかどうかはわかりません。
あなたも使うかもしれません
Function.identity().andThen(String::length).andThen(n -> n * 2)
問題は、 String::length
は必ずしもFunction
ではありません。多くの機能インターフェースに準拠できます。ターゲットタイプを提供するコンテキストで使用する必要があります。コンテキストは、割り当て、メソッドの呼び出し、キャストです。
Function
がターゲットの型付けのためだけに静的メソッドを提供できる場合、
Function.by(String::length).andThen(n->n*2)
static <T, R> Function<T, R> by(Function<T, R> f){ return f; }
たとえば、私はこのテクニックを a function interface で使用します
static <T> AsyncIterator<T> by(AsyncIterator<T> asyncIterator)
ラムダ式またはメソッド参照からAsyncIteratorを作成するための構文砂糖。
このメソッドは、引数
asyncIterator
を返すだけですが、これは少し奇妙に見えます。説明:AsyncIteratorは機能的なインターフェースであるため、3つのコンテキストでラムダ式またはメソッド参照によってインスタンスを作成できます。
// Assignment Context
AsyncIterator<ByteBuffer> asyncIter = source::read;
asyncIter.forEach(...);
// Casting Context
((AsyncIterator<ByteBuffer>)source::read)
.forEach(...);
// Invocation Context
AsyncIterator.by(source::read)
.forEach(...);
3番目のオプションは他の2つのオプションよりも見栄えがよく、それがこの方法の目的です。
キャストが使えます
Stream.of("ciao", "hola", "hello")
.map(((Function<String, Integer>) String::length)
.andThen(n -> n * 2))
.forEach(System.out::println);
プリント
8
8
10
あなたは書くことができます:
Stream.of("ciao", "hola", "hello").map(String::length).map(n -> n * 2);
reduce
関数を使用して、複数の関数を1つに結合できます。
public static void main(String[] args) {
List<Function<String, String>> normalizers = Arrays.asList(
str -> {
System.out.println(str);
return str;
},
String::toLowerCase,
str -> {
System.out.println(str);
return str;
},
String::trim,
str -> {
System.out.println(str);
return str;
});
String input = " Hello World ";
normalizers.stream()
.reduce(Function.identity(), (a, b) -> a.andThen(b))
.apply(input);
}
出力:
Hello World
hello world
hello world