web-dev-qa-db-ja.com

関数を組み合わせるためにFunctionオブジェクトとして使用されるJava8メソッド参照

Java8でメソッド参照をFunctionオブジェクトとして使用してそのメソッドを使用する方法はありますか。

Stream.of("ciao", "hola", "hello")
    .map(String::length.andThen(n -> n * 2))

この質問はStreamとは関係ありません。例として使用されているだけです。メソッドのリファレンスについてお答えしたいと思います

35
rascio

これを行う静的メソッドを作成できます。

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;     
    }
}
31
Jon Skeet

変数に保存するだけです。

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));
22
JB Nizet

キャストを使用して、インラインで必要なことを実現できるはずです。

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));

これはテストされていないため、この場合、型推論が正しく機能するかどうかはわかりません。

11
Clashsoft

あなたも使うかもしれません

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つのオプションよりも見栄えがよく、それがこの方法の目的です。

7
ZhongYu

キャストが使えます

Stream.of("ciao", "hola", "hello")
        .map(((Function<String, Integer>) String::length)
                .andThen(n -> n * 2))
        .forEach(System.out::println);

プリント

8
8
10
5
Peter Lawrey

あなたは書くことができます:

Stream.of("ciao", "hola", "hello").map(String::length).map(n -> n * 2);
4
Tunaki

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
0