572

Stringパラメータを持ち、を返すメソッドへの参照を作成する方法を知っていますint。それは次のとおりです。

Function<String, Integer>

ただし、関数が例外をスローする場合、これは機能しません。たとえば、次のように定義されています。

Integer myMethod(String s) throws IOException

この参照をどのように定義しますか?

4

26 に答える 26

233

Java 8 のデフォルト メソッドを使用して、例外を処理する新しいインターフェイスで実際に拡張Consumer(など) できます。Function

このインターフェイスを考えてみましょう (拡張Consumer):

@FunctionalInterface
public interface ThrowingConsumer<T> extends Consumer<T> {

    @Override
    default void accept(final T elem) {
        try {
            acceptThrows(elem);
        } catch (final Exception e) {
            // Implement your own exception handling logic here..
            // For example:
            System.out.println("handling an exception...");
            // Or ...
            throw new RuntimeException(e);
        }
    }

    void acceptThrows(T elem) throws Exception;

}

次に、たとえば、リストがある場合:

final List<String> list = Arrays.asList("A", "B", "C");

例外をスローするコードでそれを (たとえば で) 消費したい場合forEachは、伝統的に try/catch ブロックを設定します。

final Consumer<String> consumer = aps -> {
    try {
        // maybe some other code here...
        throw new Exception("asdas");
    } catch (final Exception ex) {
        System.out.println("handling an exception...");
    }
};
list.forEach(consumer);

しかし、この新しいインターフェイスを使用すると、ラムダ式でインスタンス化でき、コンパイラーは文句を言いません:

final ThrowingConsumer<String> throwingConsumer = aps -> {
    // maybe some other code here...
    throw new Exception("asdas");
};
list.forEach(throwingConsumer);

または、より簡潔にするためにキャストすることもできます!:

list.forEach((ThrowingConsumer<String>) aps -> {
    // maybe some other code here...
    throw new Exception("asda");
});

更新:この問題をより柔軟に解決するために使用できるErrorsと呼ばれるDurianの非常に優れたユーティリティ ライブラリ部分があるようです。たとえば、上記の実装では、エラー処理ポリシー (または) を明示的に定義しましたが、Durian のエラーでは、ユーティリティ メソッドの大規模なスイートを介してその場でポリシーを適用できます。共有してくれてありがとう、@NedTwigg!.System.out...throw RuntimeException

使用例:

list.forEach(Errors.rethrow().wrap(c -> somethingThatThrows(c)));
于 2014-12-02T14:46:01.203 に答える
70

Durian のErrorsクラスは、上記のさまざまな提案の利点の多くを兼ね備えていると思います。

プロジェクトにドリアンを含めるには、次のいずれかを実行できます。

于 2015-05-14T19:46:18.830 に答える
32

これは Java 8 に固有のものではありません。次のようなものをコンパイルしようとしています。

interface I {
    void m();
}
class C implements I {
    public void m() throws Exception {} //can't compile
}
于 2013-08-12T23:36:50.610 に答える
10

サードパーティの lib ( Vavr )を使用してもかまわない場合は、次のように記述できます。

CheckedFunction1<String, Integer> f = this::myMethod;

エラーを処理する、いわゆる Try モナドもあります。

Try(() -> f.apply("test")) // results in a Success(Integer) or Failure(Throwable)
        .map(i -> ...) // only executed on Success
        ...

詳しくはこちらをご覧ください。

免責事項: 私は Vavr の作成者です。

于 2016-02-21T21:52:33.443 に答える
7

ただし、以下のようにスローする独自の FunctionalInterface を作成できます。

@FunctionalInterface
public interface UseInstance<T, X extends Throwable> {
  void accept(T instance) throws X;
}

次に、以下に示すようにラムダまたは参照を使用して実装します。

import java.io.FileWriter;
import java.io.IOException;

//lambda expressions and the execute around method (EAM) pattern to
//manage resources

public class FileWriterEAM  {
  private final FileWriter writer;

  private FileWriterEAM(final String fileName) throws IOException {
    writer = new FileWriter(fileName);
  }
  private void close() throws IOException {
    System.out.println("close called automatically...");
    writer.close();
  }
  public void writeStuff(final String message) throws IOException {
    writer.write(message);
  }
  //...

  public static void use(final String fileName, final UseInstance<FileWriterEAM, IOException> block) throws IOException {

    final FileWriterEAM writerEAM = new FileWriterEAM(fileName);    
    try {
      block.accept(writerEAM);
    } finally {
      writerEAM.close();
    }
  }

  public static void main(final String[] args) throws IOException {

    FileWriterEAM.use("eam.txt", writerEAM -> writerEAM.writeStuff("sweet"));

    FileWriterEAM.use("eam2.txt", writerEAM -> {
        writerEAM.writeStuff("how");
        writerEAM.writeStuff("sweet");      
      });

    FileWriterEAM.use("eam3.txt", FileWriterEAM::writeIt);     

  }


 void writeIt() throws IOException{
     this.writeStuff("How ");
     this.writeStuff("sweet ");
     this.writeStuff("it is");

 }

}
于 2015-05-30T22:48:52.817 に答える
7

unthrow ラッパーを使用できます

Function<String, Integer> func1 = s -> Unthrow.wrap(() -> myMethod(s));

また

Function<String, Integer> func2 = s1 -> Unthrow.wrap((s2) -> myMethod(s2), s1);
于 2016-03-08T22:13:23.447 に答える
5

ラムダ内の Class.forName と Class.newInstance でこの問題が発生したため、次のようにしました。

public Object uncheckedNewInstanceForName (String name) {

    try {
        return Class.forName(name).newInstance();
    }
    catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}

ラムダ内では、Class.forName("myClass").newInstance() を呼び出す代わりに、uncheckedNewInstanceForName ("myClass") を呼び出しました。

于 2015-01-17T12:21:18.427 に答える
5

チェックされた例外を伝播するカスタムの戻り値の型を作成します。これは、既存の機能インターフェースを反映する新しいインターフェースを作成する代わりに、機能インターフェースのメソッドで「例外のスロー」をわずかに変更することで実現できます。

意味

CheckedValueSupplier

public static interface CheckedValueSupplier<V> {
    public V get () throws Exception;
}

チェック値

public class CheckedValue<V> {
    private final V v;
    private final Optional<Exception> opt;

    public Value (V v) {
        this.v = v;
    }

    public Value (Exception e) {
        this.opt = Optional.of(e);
    }

    public V get () throws Exception {
        if (opt.isPresent()) {
            throw opt.get();
        }
        return v;
    }

    public Optional<Exception> getException () {
        return opt;
    }

    public static <T> CheckedValue<T> returns (T t) {
        return new CheckedValue<T>(t);
    }

    public static <T> CheckedValue<T> rethrows (Exception e) {
        return new CheckedValue<T>(e);
    }

    public static <V> CheckedValue<V> from (CheckedValueSupplier<V> sup) {
        try {
            return CheckedValue.returns(sup.get());
        } catch (Exception e) {
            return Result.rethrows(e);
        }
    }

    public static <V> CheckedValue<V> escalates (CheckedValueSupplier<V> sup) {
        try {
            return CheckedValue.returns(sup.get());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

使用法

//  Don't use this pattern with FileReader, it's meant to be an
//  example.  FileReader is a Closeable resource and as such should
//  be managed in a try-with-resources block or in another safe
//  manner that will make sure it is closed properly.

//  This will not compile as the FileReader constructor throws
//  an IOException.
    Function<String, FileReader> sToFr =
        (fn) -> new FileReader(Paths.get(fn).toFile());

// Alternative, this will compile.
    Function<String, CheckedValue<FileReader>> sToFr = (fn) -> {
        return CheckedValue.from (
            () -> new FileReader(Paths.get("/home/" + f).toFile()));
    };

// Single record usage
    // The call to get() will propagate the checked exception if it exists.
    FileReader readMe = pToFr.apply("/home/README").get();


// List of records usage
    List<String> paths = ...; //a list of paths to files
    Collection<CheckedValue<FileReader>> frs =
        paths.stream().map(pToFr).collect(Collectors.toList());

// Find out if creation of a file reader failed.
    boolean anyErrors = frs.stream()
        .filter(f -> f.getException().isPresent())
        .findAny().isPresent();

どうしたの?

チェック例外をスローする単一の機能インターフェースが作成されます ( CheckedValueSupplier)。これは、チェック例外を許可する唯一の機能インターフェースになります。他のすべての機能インターフェイスは、 を利用しCheckedValueSupplierて、チェック例外をスローするコードをラップします。

このCheckedValueクラスは、チェック例外をスローするロジックを実行した結果を保持します。これにより、 のインスタンスに含まれる値にコードがアクセスしようとする時点まで、チェック済み例外の伝播が防止さCheckedValueれます。

このアプローチの問題。

  • 最初にスローされた特定のタイプを効果的に隠す「例外」をスローしています。
  • CheckedValue#get()が呼び出されるまで、例外が発生したことに気づきません。

消費者等

一部の関数インターフェイス (Consumerたとえば) は、戻り値を提供しないため、別の方法で処理する必要があります。

消費者に代わる機能

1 つのアプローチは、コンシューマーの代わりに関数を使用することです。これは、ストリームを処理するときに適用されます。

    List<String> lst = Lists.newArrayList();
// won't compile
lst.stream().forEach(e -> throwyMethod(e));
// compiles
lst.stream()
    .map(e -> CheckedValueSupplier.from(
        () -> {throwyMethod(e); return e;}))
    .filter(v -> v.getException().isPresent()); //this example may not actually run due to lazy stream behavior

エスカレート

または、いつでもエスカレートできますRuntimeException。内からのチェック済み例外のエスカレーションをカバーする他の回答がありますConsumer

消費しないでください。

機能的なインターフェイスをまとめて使用することは避け、昔ながらの for ループを使用してください。

于 2016-08-30T14:44:41.377 に答える
4

この問題は私も悩まされています。これが私がこのプロジェクトを作成した理由です。

それを使用すると、次のことができます。

final ThrowingFunction<String, Integer> f = yourMethodReferenceHere;

JDK によって定義された合計 39 のインターフェースがあり、これらはThrowing同等のものを持っています。これらはすべて@FunctionalInterface、ストリームで使用される です (ベースStreamだけでなくIntStream、 、LongStreamおよびもDoubleStream)。

そして、それらのそれぞれが非スローの対応物を拡張するため、ラムダでも直接使用できます。

myStringStream.map(f) // <-- works

デフォルトの動作では、スローするラムダがチェック済み例外をThrownByLambdaExceptionスローすると、チェック済み例外を原因として a がスローされます。したがって、それをキャプチャして原因を取得できます。

その他の機能も利用できます。

于 2014-12-29T22:00:59.317 に答える
4

すでに多くの素晴らしい回答がここに投稿されています。別の視点で問題を解決しようとしているだけです。それはちょうど私の2セントです、私がどこかで間違っているなら、私を修正してください.

FunctionalInterface の throws 句はお勧めできません

次の理由により、スロー IOException を強制するのはおそらく良い考えではないと思います

  • これは、Stream/Lambda に対するアンチパターンのように思えます。全体的な考え方は、呼び出し元が提供するコードと例外の処理方法を決定するということです。多くのシナリオでは、クライアントに IOException を適用できない場合があります。たとえば、クライアントが実際の I/O を実行するのではなく、キャッシュ/メモリから値を取得している場合です。

  • また、ストリームでの例外処理は非常に厄介です。たとえば、APIを使用すると、コードは次のようになります

               acceptMyMethod(s -> {
                    try {
                        Integer i = doSomeOperation(s);
                        return i;
                    } catch (IOException e) {
                        // try catch block because of throws clause
                        // in functional method, even though doSomeOperation
                        // might not be throwing any exception at all.
                        e.printStackTrace();
                    }
                    return null;
                });
    

    醜いですね。さらに、最初のポイントで述べたように、doSomeOperation メソッドは (クライアント/呼び出し元の実装に応じて) IOException をスローする場合とスローしない場合がありますが、FunctionalInterface メソッドの throws 句のため、常に記述する必要があります。トライキャッチ。

この API が IOException をスローすることが本当にわかっている場合はどうすればよいですか

  • おそらく、FunctionalInterface と典型的な Interface を混同しているのでしょう。この API が IOException をスローすることを知っている場合は、おそらく、いくつかのデフォルト/抽象動作も知っているでしょう。次のように、インターフェースを定義し、ライブラリを(デフォルト/抽象実装で)デプロイする必要があると思います

    public interface MyAmazingAPI {
        Integer myMethod(String s) throws IOException;
    }
    

    ただし、クライアントにはまだ try-catch 問題が存在します。API をストリームで使用する場合でも、恐ろしい try-catch ブロックで IOException を処理する必要があります。

  • 次のように、デフォルトのストリーム対応 API を提供します。

    public interface MyAmazingAPI {
        Integer myMethod(String s) throws IOException;
    
        default Optional<Integer> myMethod(String s, Consumer<? super Exception> exceptionConsumer) {
            try {
                return Optional.ofNullable(this.myMethod(s));
            } catch (Exception e) {
                if (exceptionConsumer != null) {
                    exceptionConsumer.accept(e);
                } else {
                    e.printStackTrace();
                }
            }
    
            return Optional.empty();
        }
    }
    

    デフォルトのメソッドは、消費者オブジェクトを引数として取り、例外の処理を担当します。さて、クライアントの観点からは、コードは次のようになります

    strStream.map(str -> amazingAPIs.myMethod(str, Exception::printStackTrace))
                    .filter(Optional::isPresent)
                    .map(Optional::get).collect(toList());
    

    いいね?もちろん、Exception::printStackTrace の代わりにロガーやその他の処理ロジックを使用することもできます。

  • https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#exceptionally-java.util.function.Function-のようなメソッドを公開することもでき ます。以前のメソッド呼び出しからの例外を含む別のメソッドを公開できることを意味します。不利な点は、API をステートフルにすることです。つまり、スレッド セーフを処理する必要があり、最終的にはパフォーマンス ヒットになります。ただし、考慮すべきオプションです。

于 2016-01-03T07:36:55.140 に答える
3

サードパーティのライブラリを使用してもかまわない場合は、私が貢献しているライブラリであるcyclops-reactを使用して、 FluentFunctions APIを使用して次のように記述できます

 Function<String, Integer> standardFn = FluentFunctions.ofChecked(this::myMethod);

ofChecked は jOOλ CheckedFunction を受け取り、参照をソフト化して標準 (未チェック) JDK java.util.function.Function に戻します。

または、FluentFunctions api を介してキャプチャされた関数を操作し続けることもできます!

たとえば、メソッドを実行するには、最大 5 回まで再試行し、そのステータスをログに記録します。

  FluentFunctions.ofChecked(this::myMethod)
                 .log(s->log.debug(s),e->log.error(e,e.getMessage())
                 .try(5,1000)
                 .apply("my param");
于 2016-02-24T15:56:57.817 に答える
3

これにはETを使用できます。ET は、例外変換/翻訳用の小さな Java 8 ライブラリです。

ET を使用すると、次のようになります。

// Do this once
ExceptionTranslator et = ET.newConfiguration().done();

...

// if your method returns something
Function<String, Integer> f = (t) -> et.withReturningTranslation(() -> myMethod(t));

// if your method returns nothing
Consumer<String> c = (t) -> et.withTranslation(() -> myMethod(t));

ExceptionTranslatorインスタンスはスレッドセーフであり、複数のコンポーネントで共有できます。必要に応じて、より具体的な例外変換ルール (例: FooCheckedException -> BarRuntimeException) を構成できます。他のルールが利用できない場合、チェックされた例外は自動的に に変換されRuntimeExceptionます。

(免責事項: 私は ET の作成者です)

于 2015-09-02T18:04:12.907 に答える
-7
public void frankTest() {
    int pageId= -1;

    List<Book> users= null;
    try {
        //Does Not Compile:  Object page=DatabaseConnection.getSpringConnection().queryForObject("SELECT * FROM bookmark_page", (rw, n) -> new Portal(rw.getInt("id"), "", users.parallelStream().filter(uu -> uu.getVbid() == rw.getString("user_id")).findFirst().get(), rw.getString("name")));

        //Compiles:
        Object page= DatabaseConnection.getSpringConnection().queryForObject("SELECT * FROM bookmark_page", (rw, n) -> { 
            try {
                final Book bk= users.stream().filter(bp -> { 
                    String name= null;
                    try {
                        name = rw.getString("name");
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    return bp.getTitle().equals(name); 
                }).limit(1).collect(Collectors.toList()).get(0);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return new Portal(rw.getInt("id"), "", users.get(0), rw.getString("name")); 
        } );
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}
于 2015-01-09T16:37:49.723 に答える