为内部 sub-interfaces 创建一个 "default constructor"
Creating a "default constructor" for inner sub-interfaces
好吧,标题可能比较难懂。我没有找到正确的东西。
所以,基本上我使用 Java 8 个函数来创建 Retryable API。我想要这些接口的简单实现,所以我在 Retryable 接口的每个实现中创建了一个 of(...)
方法,我们可以在其中使用 lambda 表达式,而不是手动创建一个匿名的 class.
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public interface Retryable<T, R> extends Function<T, R>{
void retrying(Exception e);
void skipping(Exception e);
int trials();
@Override
default R apply(T t) {
int trial = 0;
while (true) {
trial++;
try {
return action(t);
} catch (Exception e) {
if (trial < trials()) {
retrying(e);
} else {
skipping(e);
return null;
}
}
}
}
R action(T input) throws Exception;
interface RunnableRetryable extends Retryable<Void, Void> {
static RunnableRetryable of(Consumer<Exception> retrying, Consumer<Exception> skipping, int trials, CheckedRunnable runnable) {
return new RunnableRetryable() {
@Override
public void retrying(Exception e) {
retrying.accept(e);
}
@Override
public void skipping(Exception e) {
skipping.accept(e);
}
@Override
public int trials() {
return trials;
}
@Override
public Void action(Void v) throws Exception {
runnable.tryRun();
return null;
}
};
}
@FunctionalInterface
interface CheckedRunnable extends Runnable {
void tryRun() throws Exception;
@Override
default void run() {
try {
tryRun();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
interface ConsumerRetryable<T> extends Retryable<T, Void> {
static <T> ConsumerRetryable of(Consumer<Exception> retrying, Consumer<Exception> skipping, int trials, CheckedConsumer<T> consumer) {
return new ConsumerRetryable<T>() {
@Override
public void retrying(Exception e) {
retrying.accept(e);
}
@Override
public void skipping(Exception e) {
skipping.accept(e);
}
@Override
public int trials() {
return trials;
}
@Override
public Void action(T t) throws Exception {
consumer.tryAccept(t);
return null;
}
};
}
@FunctionalInterface
interface CheckedConsumer<T> extends Consumer<T> {
void tryAccept(T t) throws Exception;
@Override
default void accept(T t) {
try {
tryAccept(t);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
interface SupplierRetryable<T> extends Retryable<Void, T> {
static <T> SupplierRetryable of(Consumer<Exception> retrying, Consumer<Exception> skipping, int trials, CheckedSupplier<T> supplier) {
return new SupplierRetryable<T>() {
@Override
public void retrying(Exception e) {
retrying.accept(e);
}
@Override
public void skipping(Exception e) {
skipping.accept(e);
}
@Override
public int trials() {
return trials;
}
@Override
public T action(Void v) throws Exception {
return supplier.tryGet();
}
};
}
@FunctionalInterface
interface CheckedSupplier<T> extends Supplier<T> {
T tryGet() throws Exception;
@Override
default T get() {
try {
return tryGet();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
interface FunctionRetryable<T, R> extends Retryable<T, R> {
static <T, R> FunctionRetryable of(Consumer<Exception> retrying, Consumer<Exception> skipping, int trials, CheckedFunction<T, R> function) {
return new FunctionRetryable<T, R>() {
@Override
public void retrying(Exception e) {
retrying.accept(e);
}
@Override
public void skipping(Exception e) {
skipping.accept(e);
}
@Override
public int trials() {
return trials;
}
@Override
public R action(T t) throws Exception {
return function.tryApply(t);
}
};
}
@FunctionalInterface
interface CheckedFunction<T, R> extends Function<T, R> {
R tryApply(T t) throws Exception;
@Override
default R apply(T t) {
try {
return tryApply(t);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
}
但是如您所见,每个 of(...)
方法中都有很多重复代码。我可以在 Retryable 接口中创建一种 "constructor" (这不是正确的词,因为接口不能有构造函数),但我不知道如何做。有人有想法吗?
看起来你可以将其中的一些分解成一个(可能是包私有的)抽象 class:
abstract class AbstractRetryable<T, R> implements Retryable<T, R> {
private final Consumer<Exception> retrying;
private final Consumer<Exception> skipping;
private final int trials;
AbstractRetryable(Consumer<Exception> retrying,
Consumer<Exception> skipping,
int trials) {
this.retrying = Objects.requireNonNull(retrying, "retrying");
this.skipping = Objects.requireNonNull(skipping, "skipping");
this.trials = trials;
}
@Override
public void retrying(Exception x) {
retrying.accept(x);
}
@Override
public void skipping(Exception x) {
skipping.accept(x);
}
@Override
public int trials() {
return trials;
}
}
唯一的问题是您正在使用子接口,因此您无法创建既扩展抽象 class 又实现子接口的匿名 class。
然后你可以写更多(同样,可能是包私有的)subclasses:
final class RunnableRetryableImpl
extends AbstractRetryable<Void, Void>
implements RunnableRetryable {
private final CheckedRunnable runnable;
RunnableRetryableImpl(Consumer<Exception> retrying,
Consumer<Exception> skipping,
int trials,
CheckedRunnable runnable) {
super(retrying, skipping, trials);
this.runnable = Objects.requireNonNull(runnable, "runnable");
}
@Override
public Void apply(Void ignored) {
try {
runnable.tryRun();
} catch (Exception x) {
// BTW I would consider doing this.
if (x instanceof RuntimeException)
throw (RuntimeException) x;
// I would also probably write a class like:
// class RethrownException extends RuntimeException {
// RethrownException(Exception cause) {
// super(cause);
// }
// }
// This way the caller can catch a specific type if
// they want to.
// (See e.g. java.io.UncheckedIOException)
throw new RuntimeException(x);
}
return null;
}
}
或者您可以使用本地 classes:
来减少行数
static RunnableRetryable of(Consumer<Exception> retrying,
Consumer<Exception> skipping,
int trials,
CheckedRunnable runnable) {
Objects.requireNonNull(runnable, "runnable");
final class RunnableRetryableImpl
extends AbstractRetryable<Void, Void>
implements RunnableRetryable {
RunnableRetryable() {
// Avoid explicitly declaring parameters
// and passing arguments.
super(retrying, skipping, trials);
}
@Override
public Void apply(Void ignored) {
try {
runnable.tryRun();
} catch (Exception x) {
if (x instanceof RuntimeException)
throw (RuntimeException) x;
throw new RuntimeException(x);
}
return null;
}
}
return new RunnableRetryableImpl();
}
就我个人而言,我认为我只会编写包私有实现而不是本地 classes,但它肯定需要大量样板代码。
此外,作为旁注,当您编写 return 匿名 classes 的工厂时,您应该在方法本身内部使用 requireNonNull
(就像我在示例中所做的那样) of
方法)。这样一来,如果将 null
传递给该方法,该方法将抛出 NPE 而不是例如稍后调用 retrying
或 skipping
抛出 NPE。
主要问题是你的 API 爆炸。所有这些扩展 Retryable
的嵌套接口都没有添加任何功能,但一旦它们成为 API 的一部分,就需要此代码的用户处理它们。此外,它们是代码重复的原因,因为这些冗余接口中的每一个都需要自己的实现,而所有实现基本上都在做同样的事情。
去除这些过时的类型后,您可以简单地将操作实现为委托:
public interface Retryable<T, R> extends Function<T, R>{
void retrying(Exception e);
void skipping(Exception e);
int trials();
@Override default R apply(T t) {
try { return action(t); }
catch(Exception e) {
for(int trial = 1; trial < trials(); trial++) {
retrying(e);
try { return action(t); } catch (Exception next) { e=next; }
}
skipping(e);
return null;
}
}
R action(T input) throws Exception;
public static Retryable<Void, Void> of(Consumer<Exception> retrying,
Consumer<Exception> skipping, int trials, CheckedRunnable runnable) {
return of(retrying, skipping, trials, x -> { runnable.tryRun(); return null; });
}
@FunctionalInterface interface CheckedRunnable extends Runnable {
void tryRun() throws Exception;
@Override default void run() {
try { tryRun(); } catch (Exception e) { throw new RuntimeException(e); }
}
}
public static <T> Retryable<T, Void> of(Consumer<Exception> retrying,
Consumer<Exception> skipping, int trials, CheckedConsumer<T> consumer) {
return of(retrying, skipping, trials,
value -> { consumer.tryAccept(value); return null; });
}
@FunctionalInterface interface CheckedConsumer<T> extends Consumer<T> {
void tryAccept(T t) throws Exception;
@Override default void accept(T t) {
try { tryAccept(t); } catch (Exception e) { throw new RuntimeException(e); }
}
}
public static <T> Retryable<Void, T> of(Consumer<Exception> retrying,
Consumer<Exception> skipping, int trials, CheckedSupplier<T> supplier) {
return of(retrying, skipping, trials, voidArg -> { return supplier.tryGet(); });
}
@FunctionalInterface interface CheckedSupplier<T> extends Supplier<T> {
T tryGet() throws Exception;
@Override default T get() {
try { return tryGet(); }
catch (Exception e) { throw new RuntimeException(e); }
}
}
public static <T, R> Retryable<T, R> of(Consumer<Exception> retrying,
Consumer<Exception> skipping, int trials, CheckedFunction<T, R> function) {
return new Retryable<T, R>() {
@Override public void retrying(Exception e) { retrying.accept(e); }
@Override public void skipping(Exception e) { skipping.accept(e); }
@Override public int trials() { return trials; }
@Override public R action(T t) throws Exception {
return function.tryApply(t);
}
};
}
@FunctionalInterface interface CheckedFunction<T, R> extends Function<T, R> {
R tryApply(T t) throws Exception;
@Override default R apply(T t) {
try { return tryApply(t); }
catch (Exception e) { throw new RuntimeException(e); }
}
}
}
只需要一个实现 class,它必须能够处理一个参数和一个 return 值,其他的可以简单地使用适配器函数委托给它,做任何一个,删除参数或 returning null
,或两者兼而有之。
对于大多数用例,lambda 表达式的形状适合 select 正确的方法,例如
Retryable<Void,Void> r = Retryable.of(e -> {}, e -> {}, 3, () -> {});
Retryable<Void,String> s = Retryable.of(e -> {}, e -> {}, 3, () -> "foo");
Retryable<Integer,Integer> f = Retryable.of(e -> {}, e -> {}, 3, i -> i/0);
但有时,需要一点提示:
// braces required to disambiguate between Function and Consumer
Retryable<String,Void> c = Retryable.of(e->{}, e ->{}, 3,
str -> { System.out.println(str); });
好吧,标题可能比较难懂。我没有找到正确的东西。
所以,基本上我使用 Java 8 个函数来创建 Retryable API。我想要这些接口的简单实现,所以我在 Retryable 接口的每个实现中创建了一个 of(...)
方法,我们可以在其中使用 lambda 表达式,而不是手动创建一个匿名的 class.
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public interface Retryable<T, R> extends Function<T, R>{
void retrying(Exception e);
void skipping(Exception e);
int trials();
@Override
default R apply(T t) {
int trial = 0;
while (true) {
trial++;
try {
return action(t);
} catch (Exception e) {
if (trial < trials()) {
retrying(e);
} else {
skipping(e);
return null;
}
}
}
}
R action(T input) throws Exception;
interface RunnableRetryable extends Retryable<Void, Void> {
static RunnableRetryable of(Consumer<Exception> retrying, Consumer<Exception> skipping, int trials, CheckedRunnable runnable) {
return new RunnableRetryable() {
@Override
public void retrying(Exception e) {
retrying.accept(e);
}
@Override
public void skipping(Exception e) {
skipping.accept(e);
}
@Override
public int trials() {
return trials;
}
@Override
public Void action(Void v) throws Exception {
runnable.tryRun();
return null;
}
};
}
@FunctionalInterface
interface CheckedRunnable extends Runnable {
void tryRun() throws Exception;
@Override
default void run() {
try {
tryRun();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
interface ConsumerRetryable<T> extends Retryable<T, Void> {
static <T> ConsumerRetryable of(Consumer<Exception> retrying, Consumer<Exception> skipping, int trials, CheckedConsumer<T> consumer) {
return new ConsumerRetryable<T>() {
@Override
public void retrying(Exception e) {
retrying.accept(e);
}
@Override
public void skipping(Exception e) {
skipping.accept(e);
}
@Override
public int trials() {
return trials;
}
@Override
public Void action(T t) throws Exception {
consumer.tryAccept(t);
return null;
}
};
}
@FunctionalInterface
interface CheckedConsumer<T> extends Consumer<T> {
void tryAccept(T t) throws Exception;
@Override
default void accept(T t) {
try {
tryAccept(t);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
interface SupplierRetryable<T> extends Retryable<Void, T> {
static <T> SupplierRetryable of(Consumer<Exception> retrying, Consumer<Exception> skipping, int trials, CheckedSupplier<T> supplier) {
return new SupplierRetryable<T>() {
@Override
public void retrying(Exception e) {
retrying.accept(e);
}
@Override
public void skipping(Exception e) {
skipping.accept(e);
}
@Override
public int trials() {
return trials;
}
@Override
public T action(Void v) throws Exception {
return supplier.tryGet();
}
};
}
@FunctionalInterface
interface CheckedSupplier<T> extends Supplier<T> {
T tryGet() throws Exception;
@Override
default T get() {
try {
return tryGet();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
interface FunctionRetryable<T, R> extends Retryable<T, R> {
static <T, R> FunctionRetryable of(Consumer<Exception> retrying, Consumer<Exception> skipping, int trials, CheckedFunction<T, R> function) {
return new FunctionRetryable<T, R>() {
@Override
public void retrying(Exception e) {
retrying.accept(e);
}
@Override
public void skipping(Exception e) {
skipping.accept(e);
}
@Override
public int trials() {
return trials;
}
@Override
public R action(T t) throws Exception {
return function.tryApply(t);
}
};
}
@FunctionalInterface
interface CheckedFunction<T, R> extends Function<T, R> {
R tryApply(T t) throws Exception;
@Override
default R apply(T t) {
try {
return tryApply(t);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
}
但是如您所见,每个 of(...)
方法中都有很多重复代码。我可以在 Retryable 接口中创建一种 "constructor" (这不是正确的词,因为接口不能有构造函数),但我不知道如何做。有人有想法吗?
看起来你可以将其中的一些分解成一个(可能是包私有的)抽象 class:
abstract class AbstractRetryable<T, R> implements Retryable<T, R> {
private final Consumer<Exception> retrying;
private final Consumer<Exception> skipping;
private final int trials;
AbstractRetryable(Consumer<Exception> retrying,
Consumer<Exception> skipping,
int trials) {
this.retrying = Objects.requireNonNull(retrying, "retrying");
this.skipping = Objects.requireNonNull(skipping, "skipping");
this.trials = trials;
}
@Override
public void retrying(Exception x) {
retrying.accept(x);
}
@Override
public void skipping(Exception x) {
skipping.accept(x);
}
@Override
public int trials() {
return trials;
}
}
唯一的问题是您正在使用子接口,因此您无法创建既扩展抽象 class 又实现子接口的匿名 class。
然后你可以写更多(同样,可能是包私有的)subclasses:
final class RunnableRetryableImpl
extends AbstractRetryable<Void, Void>
implements RunnableRetryable {
private final CheckedRunnable runnable;
RunnableRetryableImpl(Consumer<Exception> retrying,
Consumer<Exception> skipping,
int trials,
CheckedRunnable runnable) {
super(retrying, skipping, trials);
this.runnable = Objects.requireNonNull(runnable, "runnable");
}
@Override
public Void apply(Void ignored) {
try {
runnable.tryRun();
} catch (Exception x) {
// BTW I would consider doing this.
if (x instanceof RuntimeException)
throw (RuntimeException) x;
// I would also probably write a class like:
// class RethrownException extends RuntimeException {
// RethrownException(Exception cause) {
// super(cause);
// }
// }
// This way the caller can catch a specific type if
// they want to.
// (See e.g. java.io.UncheckedIOException)
throw new RuntimeException(x);
}
return null;
}
}
或者您可以使用本地 classes:
来减少行数static RunnableRetryable of(Consumer<Exception> retrying,
Consumer<Exception> skipping,
int trials,
CheckedRunnable runnable) {
Objects.requireNonNull(runnable, "runnable");
final class RunnableRetryableImpl
extends AbstractRetryable<Void, Void>
implements RunnableRetryable {
RunnableRetryable() {
// Avoid explicitly declaring parameters
// and passing arguments.
super(retrying, skipping, trials);
}
@Override
public Void apply(Void ignored) {
try {
runnable.tryRun();
} catch (Exception x) {
if (x instanceof RuntimeException)
throw (RuntimeException) x;
throw new RuntimeException(x);
}
return null;
}
}
return new RunnableRetryableImpl();
}
就我个人而言,我认为我只会编写包私有实现而不是本地 classes,但它肯定需要大量样板代码。
此外,作为旁注,当您编写 return 匿名 classes 的工厂时,您应该在方法本身内部使用 requireNonNull
(就像我在示例中所做的那样) of
方法)。这样一来,如果将 null
传递给该方法,该方法将抛出 NPE 而不是例如稍后调用 retrying
或 skipping
抛出 NPE。
主要问题是你的 API 爆炸。所有这些扩展 Retryable
的嵌套接口都没有添加任何功能,但一旦它们成为 API 的一部分,就需要此代码的用户处理它们。此外,它们是代码重复的原因,因为这些冗余接口中的每一个都需要自己的实现,而所有实现基本上都在做同样的事情。
去除这些过时的类型后,您可以简单地将操作实现为委托:
public interface Retryable<T, R> extends Function<T, R>{
void retrying(Exception e);
void skipping(Exception e);
int trials();
@Override default R apply(T t) {
try { return action(t); }
catch(Exception e) {
for(int trial = 1; trial < trials(); trial++) {
retrying(e);
try { return action(t); } catch (Exception next) { e=next; }
}
skipping(e);
return null;
}
}
R action(T input) throws Exception;
public static Retryable<Void, Void> of(Consumer<Exception> retrying,
Consumer<Exception> skipping, int trials, CheckedRunnable runnable) {
return of(retrying, skipping, trials, x -> { runnable.tryRun(); return null; });
}
@FunctionalInterface interface CheckedRunnable extends Runnable {
void tryRun() throws Exception;
@Override default void run() {
try { tryRun(); } catch (Exception e) { throw new RuntimeException(e); }
}
}
public static <T> Retryable<T, Void> of(Consumer<Exception> retrying,
Consumer<Exception> skipping, int trials, CheckedConsumer<T> consumer) {
return of(retrying, skipping, trials,
value -> { consumer.tryAccept(value); return null; });
}
@FunctionalInterface interface CheckedConsumer<T> extends Consumer<T> {
void tryAccept(T t) throws Exception;
@Override default void accept(T t) {
try { tryAccept(t); } catch (Exception e) { throw new RuntimeException(e); }
}
}
public static <T> Retryable<Void, T> of(Consumer<Exception> retrying,
Consumer<Exception> skipping, int trials, CheckedSupplier<T> supplier) {
return of(retrying, skipping, trials, voidArg -> { return supplier.tryGet(); });
}
@FunctionalInterface interface CheckedSupplier<T> extends Supplier<T> {
T tryGet() throws Exception;
@Override default T get() {
try { return tryGet(); }
catch (Exception e) { throw new RuntimeException(e); }
}
}
public static <T, R> Retryable<T, R> of(Consumer<Exception> retrying,
Consumer<Exception> skipping, int trials, CheckedFunction<T, R> function) {
return new Retryable<T, R>() {
@Override public void retrying(Exception e) { retrying.accept(e); }
@Override public void skipping(Exception e) { skipping.accept(e); }
@Override public int trials() { return trials; }
@Override public R action(T t) throws Exception {
return function.tryApply(t);
}
};
}
@FunctionalInterface interface CheckedFunction<T, R> extends Function<T, R> {
R tryApply(T t) throws Exception;
@Override default R apply(T t) {
try { return tryApply(t); }
catch (Exception e) { throw new RuntimeException(e); }
}
}
}
只需要一个实现 class,它必须能够处理一个参数和一个 return 值,其他的可以简单地使用适配器函数委托给它,做任何一个,删除参数或 returning null
,或两者兼而有之。
对于大多数用例,lambda 表达式的形状适合 select 正确的方法,例如
Retryable<Void,Void> r = Retryable.of(e -> {}, e -> {}, 3, () -> {});
Retryable<Void,String> s = Retryable.of(e -> {}, e -> {}, 3, () -> "foo");
Retryable<Integer,Integer> f = Retryable.of(e -> {}, e -> {}, 3, i -> i/0);
但有时,需要一点提示:
// braces required to disambiguate between Function and Consumer
Retryable<String,Void> c = Retryable.of(e->{}, e ->{}, 3,
str -> { System.out.println(str); });