それでもScalaの初心者であり、次のコードを実装する方法を探しています。
@Override
public void store(InputStream source, String destination, long size) {
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(size);
final PutObjectRequest request = new PutObjectRequest(
this.configuration.getBucket(), destination, source, metadata);
new RetryableService(3) {
@Override
public void call() throws Exception {
getClient().putObject(request);
}
};
}
RetryableServiceと同じ機能を実装するのにScalaで最適な方法は何でしょうか?
それは基本的にcallメソッドをN回呼び出します。それらすべてが失敗した場合、例外が発生し、成功した場合は続行します。これは何も返しませんが、値を返すことができる別のバージョンがあり(したがって、Javaには2つのクラスがあります)、Scalaの単一のクラス/関数で実行できると思います。
何か案は?
編集する
Javaの現在の実装は次のとおりです。
public abstract class RetryableService {
private static final JobsLogger log = JobsLogger
.getLogger(RetryableService.class);
private int times;
public RetryableService() {
this(3);
}
public RetryableService(int times) {
this.times = times;
this.run();
}
private void run() {
RuntimeException lastExceptionParent = null;
int x = 0;
for (; x < this.times; x++) {
try {
this.call();
lastExceptionParent = null;
break;
} catch (Exception e) {
lastExceptionParent = new RuntimeException(e);
log.errorWithoutNotice( e, "Try %d caused exception %s", x, e.getMessage() );
try {
Thread.sleep( 5000 );
} catch (InterruptedException e1) {
log.errorWithoutNotice( e1, "Sleep inside try %d caused exception %s", x, e1.getMessage() );
}
}
}
try {
this.ensure();
} catch (Exception e) {
log.error(e, "Failed while ensure inside RetryableService");
}
if ( lastExceptionParent != null ) {
throw new IllegalStateException( String.format( "Failed on try %d of %s", x, this ), lastExceptionParent);
}
}
public void ensure() throws Exception {
// blank implementation
}
public abstract void call() throws Exception;
}
再帰+ ファーストクラス関数 by-nameパラメータ==すばらしい。
def retry[T](n: Int)(fn: => T): T = {
try {
fn
} catch {
case e =>
if (n > 1) retry(n - 1)(fn)
else throw e
}
}
使い方は次のとおりです:
retry(3) {
// insert code that may fail here
}
編集:@themelの回答に触発されたわずかな変動。1行少ないコード:-)
def retry[T](n: Int)(fn: => T): T = {
try {
fn
} catch {
case e if n > 1 =>
retry(n - 1)(fn)
}
}
もう一度編集:再帰によりスタックトレースにいくつかの呼び出しが追加されたので私は気になりました。何らかの理由で、コンパイラはキャッチハンドラで末尾再帰を最適化できませんでした。ただし、catchハンドラーにないテール再帰は最適化されます:-)
@annotation.tailrec
def retry[T](n: Int)(fn: => T): T = {
val r = try { Some(fn) } catch { case e: Exception if n > 1 => None }
r match {
case Some(x) => x
case None => retry(n - 1)(fn)
}
}
もう一度編集する:どうやら私は戻ってきて、この答えに代替を追加し続けることを趣味にするつもりです。以下Option
は、を使用return
するよりも少し簡単な末尾再帰バージョンですが、関数を短絡するために使用するのは慣用的なScalaではありません。
@annotation.tailrec
def retry[T](n: Int)(fn: => T): T = {
try {
return fn
} catch {
case e if n > 1 => // ignore
}
retry(n - 1)(fn)
}
Scala 2.10アップデート。私の趣味と同じように、時々この答えを再訪します。導入されたScala 2.10 Tryは、末尾再帰的な方法で再試行を実装するクリーンな方法を提供します。
// Returning T, throwing the exception on failure
@annotation.tailrec
def retry[T](n: Int)(fn: => T): T = {
util.Try { fn } match {
case util.Success(x) => x
case _ if n > 1 => retry(n - 1)(fn)
case util.Failure(e) => throw e
}
}
// Returning a Try[T] wrapper
@annotation.tailrec
def retry[T](n: Int)(fn: => T): util.Try[T] = {
util.Try { fn } match {
case util.Failure(_) if n > 1 => retry(n - 1)(fn)
case fn => fn
}
}
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加