为内部子接口创建“默认构造函数”

时间:2017-08-07 13:13:55

标签: java constructor interface java-8 default

好的,标题可能很难理解。我找不到正确的东西。 所以,基本上我使用Java 8函数来创建一个可重试的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界面中创建一种“构造函数”(这不是正确的单词,因为接口不能有构造函数),但我不知道如何。有人有想法吗?

2 个答案:

答案 0 :(得分:5)

主要问题是您的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); }
        }
    }
}

只需要一个实现类,它必须能够处理参数和返回值,其他人可以使用适配器函数委托给它,执行任一操作,删除参数或返回{{1}或两者兼而有之。

对于大多数用例,lambda表达式的形状适合选择正确的方法,例如

null

但有时需要一点提示:

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);

答案 1 :(得分:0)

看起来您可以将其中的一部分考虑到(可能是包私有的)抽象类中:

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而不是例如有些人打电话给retryingskipping稍后抛出NPE。