为内部子接口创建“默认构造函数”
Posted
技术标签:
【中文标题】为内部子接口创建“默认构造函数”【英文标题】:Creating a "default constructor" for inner sub-interfaces 【发布时间】:2017-08-07 13:13:55 【问题描述】:好的,标题可能很难理解。我没有找到正确的东西。
所以,基本上我使用 Java 8 函数来创建一个 Retryable API。我想要这些接口的简单实现,所以我在 Retryable 接口的每个实现中创建了一个 of(...)
方法,我们可以在其中使用 lambda 表达式,而不是手动创建匿名类。
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 接口中创建一种“构造函数”(这不是正确的词,因为接口不能有构造函数),但我不知道如何。有人有想法吗?
【问题讨论】:
我看不出这些冗余内部接口存在的理由。对于不同参数化的Retryable
s,您只有多个工厂方法。
【参考方案1】:
主要问题是您的 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);
只需要一个实现类,它必须能够处理参数和返回值,其他的可以简单地使用适配器函数委托给它,要么删除参数,要么返回null
,或两者兼而有之。
对于大多数用例,lambda 表达式的形状适合选择正确的方法,例如
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); );
【讨论】:
你完全正确。我看得太远了。现在很清楚了。谢谢! @Holger 我们也有重试机制(谁没有?),但是这样做的 OP 想法和您的回答可能会给我们的代码带来新的形状......【参考方案2】:看起来您可以将其中的一些因素考虑到(可能是包私有的)抽象类中:
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;
唯一的问题是您使用的是子接口,因此您无法创建既扩展抽象类又实现子接口的匿名类。
然后您可以编写更多(同样,可能是包私有的)子类:
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;
或者你可以通过使用本地类来减少行数:
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();
就个人而言,我认为我只会编写包私有实现而不是本地类,但它肯定需要相当多的样板代码。
另外,作为旁注,当您编写返回匿名类的工厂时,您应该在方法本身内部使用requireNonNull
(就像我在示例of
方法中所做的那样)。这样,如果 null
被传递给该方法,该方法将抛出 NPE 而不是例如有些人打电话给retrying
或skipping
一段时间后抛出NPE。
【讨论】:
您可以简单地使用该类型创建第二个catch
子句,而不是在 catch
子句中执行 instanceof
,例如catch(RuntimeException|Error unchecked) throw unchecked; catch(Throwable checked) throw new RuntimeException(checked);
以上是关于为内部子接口创建“默认构造函数”的主要内容,如果未能解决你的问题,请参考以下文章
此类未定义公共默认构造函数,或构造函数引发异常。内部异常:java.lang.InstantiationException