所以我试图重构以下代码:
/**
* Returns the duration from the config file.
*
* @return The duration.
*/
private Duration durationFromConfig() {
try {
return durationFromConfigInner();
} catch (IOException ex) {
throw new IllegalStateException("The config file (\"" + configFile + "\") has not been found.");
}
}
/**
* Returns the duration from the config file.
*
* Searches the log file for the first line indicating the config entry for this instance.
*
* @return The duration.
* @throws FileNotFoundException If the config file has not been found.
*/
private Duration durationFromConfigInner() throws IOException {
String entryKey = subClass.getSimpleName();
configLastModified = Files.getLastModifiedTime(configFile);
String entryValue = ConfigFileUtils.readFileEntry(configFile, entryKey);
return Duration.of(entryValue);
}
我想出了以下内容:
private <T> T getFromConfig(final Supplier<T> supplier) {
try {
return supplier.get();
} catch (IOException ex) {
throw new IllegalStateException("The config file (\"" + configFile + "\") has not been found.");
}
}
然而,由于Supplier
无法抛出IOException
,因此无法编译(显然)。有以任何方式我可以将其添加到getFromConfig
的方法声明中吗?
或者是唯一的方法,如下所示?
@FunctionalInterface
public interface SupplierWithIO<T> extends Supplier<T> {
@Override
@Deprecated
default public T get() {
throw new UnsupportedOperationException();
}
public T getWithIO() throws IOException;
}
更新,我刚刚意识到Supplier
界面是真正简单界面,因为它只有get()
方法。我扩展Supplier
的原因是为了预先扩展基本功能,例如默认方法。
答案 0 :(得分:14)
在lambda邮件列表中,这是throughly discussed。正如你所看到的,Brian Goetz建议选择编写自己的组合器:
或者你可以编写自己的琐碎组合器:
static<T> Block<T> exceptionWrappingBlock(Block<T> b) { return e -> { try { b.accept(e); } catch (Exception e) { throw new RTE(e); } }; }
你可以写一次,少花点时间来写你的 原始电子邮件。对于您使用的每种SAM都类似一次。
我宁愿把这看作是&#34;玻璃满99%&#34;而不是 替代。并非所有问题都需要新的语言功能 解决方案。 (更不用说新的语言功能总是会导致 新问题。)
在那些日子里,Consumer界面被称为Block。
我认为这与上面Marko建议的JB Nizet's answer相对应。
后来Brian explains why这是以这种方式设计的(问题的原因)
是的,您必须提供自己的特殊SAM。但是后来lambda 转换对他们来说很好。
EG讨论了额外的语言和图书馆支持 问题,最后觉得这是一个不好的成本/收益 折衷。
基于库的解决方案导致SAM类型爆发2倍(特殊情况 vs not),它与现有的组合爆炸相互作用很严重 原始专业化。
基于语言的可用解决方案是a的输家 复杂性/价值权衡。虽然有一些选择 我们将继续探索的解决方案 - 尽管显然不是 8,也许不是9。
与此同时,你有工具可以做你想做的事。我明白了 你更喜欢我们为你提供最后一英里(其次,你的 请求真的是一个薄薄的请求&#34;为什么不是你 已放弃已检查的例外&#34;),但我认为目前 国家允许你完成你的工作。
答案 1 :(得分:9)
如果您这样做,您将无法将其用作Supplier
,因为它只会抛出UnsupportedOperationException。
考虑到上述情况,为什么不创建新界面并在其中声明getWithIO
方法?
@FunctionalInterface
public interface SupplierWithIO<T> {
public T getWithIO() throws IOException;
}
也许有些东西比旧式Java界面更好?旧式Java并没有因为现在的Java 8而消失。
答案 2 :(得分:6)
考虑这个通用解决方案:
// We need to describe supplier which can throw exceptions
@FunctionalInterface
public interface ThrowingSupplier<T> {
T get() throws Exception;
}
// Now, wrapper
private <T> T callMethod(ThrowingSupplier<T> supplier) {
try {
return supplier.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
return null;
}
// And usage example
String methodThrowsException(String a, String b, String c) throws Exception {
// do something
}
String result = callMethod(() -> methodThrowsException(x, y, z));
答案 3 :(得分:1)
我添加了自己的解决方案,而不是直接回答我的问题,在一些修订后引入了以下内容:
@FunctionalInterface
public interface CheckedSupplier<T, E extends Exception> {
public T get() throws E;
}
private <R> R getFromConfig(final String entryKey, final Function<String, R> converter) throws IOException {
Objects.requireNonNull(entryKey);
Objects.requireNonNull(converter);
configLastModified = Files.getLastModifiedTime(configFile);
return ConfigFileUtils.readFileEntry(configFile, entryKey, converter);
}
private <T> T handleIOException(final CheckedSupplier<T, IOException> supplier) {
Objects.requireNonNull(supplier);
try {
return supplier.get();
} catch (IOException ex) {
throw new IllegalStateException("The config file (\"" + configFile + "\") has not been found.");
}
}
这是所有一次性声明,现在我添加了两个调用代码变体:
private Duration durationFromConfig() {
return handleIOException(() -> getFromConfig(subClass.getSimpleName(), Duration::of));
}
private int threadsFromConfig() {
return handleIOException(() -> getFromConfig(subClass.getSimpleName() + "Threads", Integer::parseInt));
}
我对将IOException
转换为UncheckedIOException
感到不满意,因为:
IOException
的每个方法的未经检查的变体。IOException
,而不是依赖于希望你不要忘记捕捉UncheckedIOException
。答案 4 :(得分:1)
由于我在这个问题上还有一点要点,我决定加上我的答案。
您可以选择编写便捷方法:
使用第一种方法,每个功能方法签名需要一个便捷方法,而使用第二种方法,您需要总共两种方法(原始返回方法除外):
static <T> T uncheckCall(Callable<T> callable) {
try { return callable.call(); }
catch (Exception e) { return sneakyThrow(e); }
}
static void uncheckRun(RunnableExc r) {
try { r.run(); } catch (Exception e) { sneakyThrow(e); }
}
interface RunnableExc { void run() throws Exception; }
这允许您插入对其中一个方法的调用,这些方法传入一个nullary lambda,但是关闭外部lambda的任何参数,您将传递给原始方法。这样您就可以在没有样板的情况下利用自动lambda转换语言功能。
例如,你可以写
stream.forEachOrdered(o -> uncheckRun(() -> out.write(o)));
与
相比stream.forEachOrdered(uncheckWrapOneArg(o -> out.write(o)));
我还发现,重载所有lambda签名的包装方法名称通常会导致不明确的lambda表达式错误。因此,您需要更长的不同名称,从而导致char-for-char的代码比上述方法更长。
最后,请注意,所述方法仍然不能排除编写重用uncheckedRun/Call
的简单包装方法,但我发现它简直无趣,因为节省的费用最多可以忽略不计。
答案 5 :(得分:0)
我们也可以使用这种通用解决方案。我们通过这种方法处理的任何类型的异常。
@FunctionalInterface
public interface CheckedCall<T, E extends Throwable> {
T call() throws E;
}
public <T> T logTime(CheckedCall<T, Exception> block) throws Exception {
Stopwatch timer = Stopwatch.createStarted();
try {
T result = block.call();
System.out.println(timer.stop().elapsed(TimeUnit.MILLISECONDS));
return result;
} catch (Exception e) {
throw e;
}
}