java - 实现像这样的可重试调用的 Scala 方法是什么?

标签 java scala functional-programming

仍然是 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);
        }
    };

}

在 Scala 中实现与 RetryableService 相同的功能的最佳方法是什么?

它基本上调用 call 方法 N 次,如果它们都失败,则引发异常,如果它们成功则继续。这个不返回任何东西,但是我有另一个版本允许返回一个值(所以,我在 Java 中有两个类),我相信我可以在 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;

}

最佳答案

递归 + 一等函数 按名称参数 == 真棒。

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 启发的轻微变化的回答。少了一行代码:-)

def retry[T](n: Int)(fn: => T): T = {
  try {
    fn
  } catch {
    case e if n > 1 =>
      retry(n - 1)(fn)
  }
}

再次编辑:递归让我感到困扰,因为它向堆栈跟踪添加了几个调用。由于某种原因,编译器无法优化 catch 处理程序中的尾递归。但是,不在 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
  }
}

关于java - 实现像这样的可重试调用的 Scala 方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7930814/

相关文章:

java - 从 toString 转换回 Object

scala - 检查集合是否有序的惯用构造

database - 在 Scala 中使用哪个数据库(以及需要的示例)

scala - 是否可以从 Tree 和 MethodSymbol 生成 Apply?

scala - 如何在 Scala 中实现 Future as Applicative?

python - 如何将部分函数绑定(bind)为 Python 类实例上的方法

haskell - 使用变形来忘记 Cofree 注释

java - 使用自定义上下文路径的 Spring Boot - 无法访问静态 Web 文件

java - 如何将对象或分组参数传递给 SQL IN 子句?

java - Java的 "System.out.println()"什么时候重载了?