我知道如何创建对具有String
参数并返回int
的方法的引用,它是:
Function<String, Integer>
但是,如果该函数抛出异常,则该方法不起作用,例如它被定义为:
Integer myMethod(String s) throws IOException
我如何定义此引用?
答案 0 :(得分:352)
您需要执行以下操作之一。
如果是你的代码,那么定义你自己的声明已检查异常的功能接口:
@FunctionalInterface
public interface CheckedFunction<T, R> {
R apply(T t) throws IOException;
}
并使用它:
void foo (CheckedFunction f) { ... }
否则,将Integer myMethod(String s)
包装在未声明已检查异常的方法中:
public Integer myWrappedMethod(String s) {
try {
return myMethod(s);
}
catch(IOException e) {
throw new UncheckedIOException(e);
}
}
然后:
Function<String, Integer> f = (String t) -> myWrappedMethod(t);
或:
Function<String, Integer> f =
(String t) -> {
try {
return myMethod(t);
}
catch(IOException e) {
throw new UncheckedIOException(e);
}
};
答案 1 :(得分:178)
您实际上可以使用处理异常的新界面扩展Consumer
(和Function
等) - 使用Java 8的默认方法!
考虑这个界面(扩展Consumer
):
@FunctionalInterface
public interface ThrowingConsumer<T> extends Consumer<T> {
@Override
default void accept(final T elem) {
try {
acceptThrows(elem);
} catch (final Exception e) {
// Implement your own exception handling logic here..
// For example:
System.out.println("handling an exception...");
// Or ...
throw new RuntimeException(e);
}
}
void acceptThrows(T elem) throws Exception;
}
然后,例如,如果您有一个列表:
final List<String> list = Arrays.asList("A", "B", "C");
如果你想使用一些抛出异常的代码来使用它(例如forEach
),你通常会设置一个try / catch块:
final Consumer<String> consumer = aps -> {
try {
// maybe some other code here...
throw new Exception("asdas");
} catch (final Exception ex) {
System.out.println("handling an exception...");
}
};
list.forEach(consumer);
但是使用这个新接口,您可以使用lambda表达式对其进行实例化,编译器不会抱怨:
final ThrowingConsumer<String> throwingConsumer = aps -> {
// maybe some other code here...
throw new Exception("asdas");
};
list.forEach(throwingConsumer);
或者甚至只是把它变得更简洁!:
list.forEach((ThrowingConsumer<String>) aps -> {
// maybe some other code here...
throw new Exception("asda");
});
更新:看起来Durian Errors的一个非常好的实用程序库部分名为sharing it,它可以用来更灵活地解决这个问题。例如,在我上面的实现中,我明确定义了错误处理策略(System.out...
或throw RuntimeException
),而榴莲的错误允许您通过以下方式动态应用策略大套实用方法。感谢{{3}},@ NedTwigg!
样本用法:
list.forEach(Errors.rethrow().wrap(c -> somethingThatThrows(c)));
答案 2 :(得分:57)
我认为Durian's Errors
class结合了上述各种建议的许多优点。
要在项目中加入榴莲,您可以:
com.diffplug.durian:durian:3.3.0
或jcenter import turtle
t = turtle.Pen()
t.forward(50)
Throwing.java
答案 3 :(得分:27)
这不是特定于Java 8.您正在尝试编译等效于:
的内容interface I {
void m();
}
class C implements I {
public void m() throws Exception {} //can't compile
}
答案 4 :(得分:12)
免责声明:我还没有使用过Java 8,只读过它。
Function<String, Integer>
不会抛出IOException
,因此您无法在throws IOException
中添加任何代码。如果您正在调用期望Function<String, Integer>
的方法,那么传递给该方法的lambda不能抛出IOException
,句点。你可以写这样的lambda(我认为这是lambda语法,不确定):
(String s) -> {
try {
return myMethod(s);
} catch (IOException ex) {
throw new RuntimeException(ex);
// (Or do something else with it...)
}
}
或者,如果您传递lambda的方法是您自己编写的方法,则可以定义新的功能接口并将其用作参数类型而不是Function<String, Integer>
:
public interface FunctionThatThrowsIOException<I, O> {
O apply(I input) throws IOException;
}
答案 5 :(得分:7)
如果您不介意使用第三方库(Vavr),您可以写
CheckedFunction1<String, Integer> f = this::myMethod;
它还有所谓的Try monad来处理错误:
Try(() -> f.apply("test")) // results in a Success(Integer) or Failure(Throwable)
.map(i -> ...) // only executed on Success
...
请阅读更多here。
免责声明:我是Vavr的创作者。
答案 6 :(得分:6)
您可以使用unthrow wrapper
Function<String, Integer> func1 = s -> Unthrow.wrap(() -> myMethod(s));
或
Function<String, Integer> func2 = s1 -> Unthrow.wrap((s2) -> myMethod(s2), s1);
答案 7 :(得分:5)
然而,你可以创建自己的FunctionalInterface,抛出如下..
@FunctionalInterface
public interface UseInstance<T, X extends Throwable> {
void accept(T instance) throws X;
}
然后使用Lambdas或引用实现它,如下所示。
import java.io.FileWriter;
import java.io.IOException;
//lambda expressions and the execute around method (EAM) pattern to
//manage resources
public class FileWriterEAM {
private final FileWriter writer;
private FileWriterEAM(final String fileName) throws IOException {
writer = new FileWriter(fileName);
}
private void close() throws IOException {
System.out.println("close called automatically...");
writer.close();
}
public void writeStuff(final String message) throws IOException {
writer.write(message);
}
//...
public static void use(final String fileName, final UseInstance<FileWriterEAM, IOException> block) throws IOException {
final FileWriterEAM writerEAM = new FileWriterEAM(fileName);
try {
block.accept(writerEAM);
} finally {
writerEAM.close();
}
}
public static void main(final String[] args) throws IOException {
FileWriterEAM.use("eam.txt", writerEAM -> writerEAM.writeStuff("sweet"));
FileWriterEAM.use("eam2.txt", writerEAM -> {
writerEAM.writeStuff("how");
writerEAM.writeStuff("sweet");
});
FileWriterEAM.use("eam3.txt", FileWriterEAM::writeIt);
}
void writeIt() throws IOException{
this.writeStuff("How ");
this.writeStuff("sweet ");
this.writeStuff("it is");
}
}
答案 8 :(得分:5)
你可以。
扩展@marcg&#39; s eText.setError(message);
eText.getBackground().setColorFilter(Color.RED, PorterDuff.Mode.SRC_ATOP);
eText.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
eText.getBackground().setColorFilter(
ctx.getResources().getColor(R.color.dark_green), PorterDuff.Mode.SRC_ATOP);
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {}
@Override
public void afterTextChanged(Editable s) {}
});
并在必要时添加通用canvas.drawColor(Color.CYAN);
Paint p = new Paint();
// smooths
p.setAntiAlias(true);
p.setColor(Color.RED);
p.setStyle(Paint.Style.STROKE);
p.setStrokeWidth(4.5f);
canvas.drawRect(10, 10, 30, 30, p);
:这样,编译器会强制你再次添加throw子句和所有内容,就好像你可以在java 8的流中原生地抛出已检查的异常。
UtilException
答案 9 :(得分:4)
Sneaky throw idiom启用绕过CheckedException
Lambda表达式。将CheckedException
包裹在RuntimeException
中并不适合严格的错误处理。
它可以用作Java集合中使用的Consumer
函数。
以下是jib's answer的简单和改进版本。
import static Throwing.rethrow;
@Test
public void testRethrow() {
thrown.expect(IOException.class);
thrown.expectMessage("i=3");
Arrays.asList(1, 2, 3).forEach(rethrow(e -> {
int i = e.intValue();
if (i == 3) {
throw new IOException("i=" + i);
}
}));
}
这只是在 rethrow 中包含lambda。它使CheckedException
重新抛出lambda中抛出的任何Exception
。
public final class Throwing {
private Throwing() {}
@Nonnull
public static <T> Consumer<T> rethrow(@Nonnull final ThrowingConsumer<T> consumer) {
return consumer;
}
/**
* The compiler sees the signature with the throws T inferred to a RuntimeException type, so it
* allows the unchecked exception to propagate.
*
* http://www.baeldung.com/java-sneaky-throws
*/
@SuppressWarnings("unchecked")
@Nonnull
public static <E extends Throwable> void sneakyThrow(@Nonnull Throwable ex) throws E {
throw (E) ex;
}
}
查找完整的代码和单元测试here。
答案 10 :(得分:4)
这个问题一直困扰着我;这就是我创建this project。
的原因有了它,你可以这样做:
final ThrowingFunction<String, Integer> f = yourMethodReferenceHere;
JDK定义了39个接口,其中有Throwing
个等价物;这些都是在流中使用的@FunctionalInterface
(基数Stream
,但IntStream
,LongStream
和DoubleStream
)。
当他们每个人都扩展他们的非投掷对手时,你也可以直接在lambdas中使用它们:
myStringStream.map(f) // <-- works
默认行为是当你的抛出lambda抛出一个已检查的异常时,抛出ThrownByLambdaException
并将检查的异常作为原因。因此,您可以捕获并获得原因。
其他功能也可用。
答案 11 :(得分:4)
我在lambda中遇到Class.forName和Class.newInstance这个问题,所以我只是这样做了:
public Object uncheckedNewInstanceForName (String name) {
try {
return Class.forName(name).newInstance();
}
catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
在lambda中,而不是调用Class.forName(“myClass”)。newInstance()我刚调用uncheckedNewInstanceForName(“myClass”)
答案 12 :(得分:4)
这里已经发布了很多很棒的回复。只是试图用不同的视角来解决问题。它只是我的2美分,请纠正我,如果我在某处错了。
FunctionalInterface中的Throws子句不是一个好主意
我认为由于以下原因,强制执行抛出IOException可能不是一个好主意
这对我来说就像Stream / Lambda的反模式。整个想法是调用者将决定提供什么代码以及如何处理异常。在许多情况下,IOException可能不适用于客户端。例如,如果客户端从缓存/内存获取值而不是执行实际I / O.
此外,流中的异常处理变得非常可怕。例如,如果我使用您的API
,我的代码就会如此 acceptMyMethod(s -> {
try {
Integer i = doSomeOperation(s);
return i;
} catch (IOException e) {
// try catch block because of throws clause
// in functional method, even though doSomeOperation
// might not be throwing any exception at all.
e.printStackTrace();
}
return null;
});
丑陋不是吗?而且,正如我在第一点中提到的那样,doSomeOperation方法可能会抛出IOException(取决于客户端/调用者的实现),但由于FunctionalInterface方法中的throws子句,我总是要编写的try-catch。 如果我真的知道此API会抛出IOException
,该怎么办?然后我们可能会将FunctionalInterface与典型的接口混淆。如果你知道这个API会抛出IOException,那么很可能你也知道一些默认/抽象行为。我认为您应该定义一个接口并部署您的库(使用默认/抽象实现),如下所示
public interface MyAmazingAPI {
Integer myMethod(String s) throws IOException;
}
但是,客户端仍然存在try-catch问题。如果我在流中使用你的API,我仍然需要在hideous try-catch块中处理IOException。
提供默认的流友好API,如下所示
public interface MyAmazingAPI {
Integer myMethod(String s) throws IOException;
default Optional<Integer> myMethod(String s, Consumer<? super Exception> exceptionConsumer) {
try {
return Optional.ofNullable(this.myMethod(s));
} catch (Exception e) {
if (exceptionConsumer != null) {
exceptionConsumer.accept(e);
} else {
e.printStackTrace();
}
}
return Optional.empty();
}
}
默认方法将使用者对象作为参数,它将负责处理异常。现在,从客户的角度来看,代码看起来像这样
strStream.map(str -> amazingAPIs.myMethod(str, Exception::printStackTrace))
.filter(Optional::isPresent)
.map(Optional::get).collect(toList());
好的,对吗?当然,可以使用记录器或其他处理逻辑来代替Exception :: printStackTrace。 您还可以公开类似于https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#exceptionally-java.util.function.Function-的方法。这意味着您可以公开另一个方法,该方法将包含先前方法调用的异常。缺点是你现在正在使你的API有状态,这意味着你需要处理线程安全,并最终成为性能损失。只是一个考虑的选择。
答案 13 :(得分:3)
您可以使用ET。 ET是一个用于异常转换/转换的小型Java 8库。
使用ET看起来像这样:
// Do this once
ExceptionTranslator et = ET.newConfiguration().done();
...
// if your method returns something
Function<String, Integer> f = (t) -> et.withReturningTranslation(() -> myMethod(t));
// if your method returns nothing
Consumer<String> c = (t) -> et.withTranslation(() -> myMethod(t));
ExceptionTranslator
个实例是线程安全的,可以由多个组件共享。如果您愿意,可以配置更具体的异常转换规则(例如FooCheckedException -> BarRuntimeException
)。
如果没有其他规则可用,则已检查的异常会自动转换为RuntimeException
。
(免责声明:我是ET的作者)
答案 14 :(得分:3)
使用Function包装器的另一个解决方案是返回结果包装器的实例,比如Success,如果一切顺利,则返回一个实例,比如说Failure。
一些澄清事情的代码:
public interface ThrowableFunction<A, B> {
B apply(A a) throws Exception;
}
public abstract class Try<A> {
public static boolean isSuccess(Try tryy) {
return tryy instanceof Success;
}
public static <A, B> Function<A, Try<B>> tryOf(ThrowableFunction<A, B> function) {
return a -> {
try {
B result = function.apply(a);
return new Success<B>(result);
} catch (Exception e) {
return new Failure<>(e);
}
};
}
public abstract boolean isSuccess();
public boolean isError() {
return !isSuccess();
}
public abstract A getResult();
public abstract Exception getError();
}
public class Success<A> extends Try<A> {
private final A result;
public Success(A result) {
this.result = result;
}
@Override
public boolean isSuccess() {
return true;
}
@Override
public A getResult() {
return result;
}
@Override
public Exception getError() {
return new UnsupportedOperationException();
}
@Override
public boolean equals(Object that) {
if(!(that instanceof Success)) {
return false;
}
return Objects.equal(result, ((Success) that).getResult());
}
}
public class Failure<A> extends Try<A> {
private final Exception exception;
public Failure(Exception exception) {
this.exception = exception;
}
@Override
public boolean isSuccess() {
return false;
}
@Override
public A getResult() {
throw new UnsupportedOperationException();
}
@Override
public Exception getError() {
return exception;
}
}
一个简单的用例:
List<Try<Integer>> result = Lists.newArrayList(1, 2, 3).stream().
map(Try.<Integer, Integer>tryOf(i -> someMethodThrowingAnException(i))).
collect(Collectors.toList());
答案 15 :(得分:2)
我正在做的是允许用户在发生异常时提供他实际想要的值。 所以我看起来像这样的东西
public static <T, R> Function<? super T, ? extends R> defaultIfThrows(FunctionThatThrows<? super T, ? extends R> delegate, R defaultValue) {
return x -> {
try {
return delegate.apply(x);
} catch (Throwable throwable) {
return defaultValue;
}
};
}
@FunctionalInterface
public interface FunctionThatThrows<T, R> {
R apply(T t) throws Throwable;
}
然后可以这样调用:
defaultIfThrows(child -> child.getID(), null)
答案 16 :(得分:2)
创建一个自定义返回类型,该类型将传播已检查的异常。这是创建一个新接口的替代方法,该接口通过稍微修改&#34;抛出异常来反映现有的功能接口。关于功能界面的方法。
public static interface CheckedValueSupplier<V> {
public V get () throws Exception;
}
public class CheckedValue<V> {
private final V v;
private final Optional<Exception> opt;
public Value (V v) {
this.v = v;
}
public Value (Exception e) {
this.opt = Optional.of(e);
}
public V get () throws Exception {
if (opt.isPresent()) {
throw opt.get();
}
return v;
}
public Optional<Exception> getException () {
return opt;
}
public static <T> CheckedValue<T> returns (T t) {
return new CheckedValue<T>(t);
}
public static <T> CheckedValue<T> rethrows (Exception e) {
return new CheckedValue<T>(e);
}
public static <V> CheckedValue<V> from (CheckedValueSupplier<V> sup) {
try {
return CheckedValue.returns(sup.get());
} catch (Exception e) {
return Result.rethrows(e);
}
}
public static <V> CheckedValue<V> escalates (CheckedValueSupplier<V> sup) {
try {
return CheckedValue.returns(sup.get());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
// Don't use this pattern with FileReader, it's meant to be an
// example. FileReader is a Closeable resource and as such should
// be managed in a try-with-resources block or in another safe
// manner that will make sure it is closed properly.
// This will not compile as the FileReader constructor throws
// an IOException.
Function<String, FileReader> sToFr =
(fn) -> new FileReader(Paths.get(fn).toFile());
// Alternative, this will compile.
Function<String, CheckedValue<FileReader>> sToFr = (fn) -> {
return CheckedValue.from (
() -> new FileReader(Paths.get("/home/" + f).toFile()));
};
// Single record usage
// The call to get() will propagate the checked exception if it exists.
FileReader readMe = pToFr.apply("/home/README").get();
// List of records usage
List<String> paths = ...; //a list of paths to files
Collection<CheckedValue<FileReader>> frs =
paths.stream().map(pToFr).collect(Collectors.toList());
// Find out if creation of a file reader failed.
boolean anyErrors = frs.stream()
.filter(f -> f.getException().isPresent())
.findAny().isPresent();
添加&#34;抛出异常&#34; JDK中的每个功能接口都会以最令人发指的方式违反DRY原则。为了避免这种情况,会创建一个抛出已检查异常的单个功能接口(CheckedValueSupplier
)。这将是唯一允许检查异常的功能接口。所有其他功能接口将利用CheckedValueSupplier
来包装引发检查异常的任何代码。
CheckedValue
类将保存执行抛出已检查异常的任何逻辑的结果。这可以防止已检查异常的传播,直到代码尝试访问CheckedValue
实例所包含的值为止。
CheckedValue#get()
之前发生了异常。某些功能界面(例如Consumer
)必须以不同的方式处理,因为它们不提供返回值。
一种方法是使用函数而不是消费者,这在处理流时适用。
List<String> lst = Lists.newArrayList();
// won't compile
lst.stream().forEach(e -> throwyMethod(e));
// compiles
lst.stream()
.map(e -> CheckedValueSupplier.from(
() -> {throwyMethod(e); return e;}))
.filter(v -> v.getException().isPresent()); //this example may not actually run due to lazy stream behavior
或者,您始终可以升级到RuntimeException
。还有其他答案可以涵盖Consumer
内已检查例外的升级。
只需避免功能性接口,并使用良好的for循环。
答案 17 :(得分:2)
默认情况下,Java 8 Function不允许抛出异常,并且如多个答案所示,有很多方法可以实现它,一种方法是:
@FunctionalInterface
public interface FunctionWithException<T, R, E extends Exception> {
R apply(T t) throws E;
}
定义为:
private FunctionWithException<String, Integer, IOException> myMethod = (str) -> {
if ("abc".equals(str)) {
throw new IOException();
}
return 1;
};
在调用方法中添加throws
或try/catch
相同的异常。
答案 18 :(得分:1)
如果您不介意使用第三方库({3}},我参与的库,您可以使用cyclops-react API撰写
Function<String, Integer> standardFn = FluentFunctions.ofChecked(this::myMethod);
ofChecked接受一个jOOλCheckedFunction并将软化的参考返回给标准(未经检查)的JDK java.util.function.Function。
或者,您可以通过FluentFunctions api!
继续使用捕获的功能例如,要执行您的方法,最多重试5次并记录它的状态,您可以写
FluentFunctions.ofChecked(this::myMethod)
.log(s->log.debug(s),e->log.error(e,e.getMessage())
.try(5,1000)
.apply("my param");
答案 19 :(得分:0)
一些提供的解决方案使用E的泛型参数来传递抛出的异常类型。
更进一步,而不是传递异常类型,传入异常类型的消费者,如...
Consumer<E extends Exception>
您可以创建Consumer<Exception>
的几个可重用的变体,这些变体将涵盖应用程序的常见异常处理需求。
答案 20 :(得分:0)
我会做一些通用的事情:
public interface Lambda {
@FunctionalInterface
public interface CheckedFunction<T> {
T get() throws Exception;
}
public static <T> T handle(CheckedFunction<T> supplier) {
try {
return supplier.get();
} catch (Exception exception) {
throw new RuntimeException(exception);
}
}
}
用法:
Lambda.handle(() -> method());
答案 21 :(得分:0)
使用Jool Library
或jOOλ library
说JOOQ
。它不仅提供未经检查的异常处理接口,还为Seq类提供了许多有用的方法。
此外,它包含最多16个参数的功能接口。此外,它提供了在不同场景中使用的Tuple类。
特别是在org.jooq.lambda.fi.util.function
包的库查找中。它包含来自Java-8的所有接口,并带有Checked prepended。请参阅以下内容以供参考: -
答案 22 :(得分:0)
我是一个微型库的作者,它具有一些泛型魔术,可以将任何Java异常抛到任何地方,而无需捕获它们或将它们包装到RuntimeException
中。
用法:
unchecked(() -> methodThrowingCheckedException())
public class UncheckedExceptions {
/**
* throws {@code exception} as unchecked exception, without wrapping exception.
*
* @return will never return anything, return type is set to {@code exception} only to be able to write <code>throw unchecked(exception)</code>
* @throws T {@code exception} as unchecked exception
*/
@SuppressWarnings("unchecked")
public static <T extends Throwable> T unchecked(Exception exception) throws T {
throw (T) exception;
}
@FunctionalInterface
public interface UncheckedFunction<R> {
R call() throws Exception;
}
/**
* Executes given function,
* catches and rethrows checked exceptions as unchecked exceptions, without wrapping exception.
*
* @return result of function
* @see #unchecked(Exception)
*/
public static <R> R unchecked(UncheckedFunction<R> function) {
try {
return function.call();
} catch (Exception e) {
throw unchecked(e);
}
}
@FunctionalInterface
public interface UncheckedMethod {
void call() throws Exception;
}
/**
* Executes given method,
* catches and rethrows checked exceptions as unchecked exceptions, without wrapping exception.
*
* @see #unchecked(Exception)
*/
public static void unchecked(UncheckedMethod method) {
try {
method.call();
} catch (Exception e) {
throw unchecked(e);
}
}
}
答案 23 :(得分:0)
如果您有龙目岛,可以使用@SneakyThrows注释您的方法
SneakyThrow不会默默吞咽,包装到RuntimeException或以其他方式修改列出的已检查异常类型的任何异常。 JVM不检查所检查的异常系统的一致性。 javac可以,并且此注释使您可以选择退出其机制。
答案 24 :(得分:-7)
public void frankTest() {
int pageId= -1;
List<Book> users= null;
try {
//Does Not Compile: Object page=DatabaseConnection.getSpringConnection().queryForObject("SELECT * FROM bookmark_page", (rw, n) -> new Portal(rw.getInt("id"), "", users.parallelStream().filter(uu -> uu.getVbid() == rw.getString("user_id")).findFirst().get(), rw.getString("name")));
//Compiles:
Object page= DatabaseConnection.getSpringConnection().queryForObject("SELECT * FROM bookmark_page", (rw, n) -> {
try {
final Book bk= users.stream().filter(bp -> {
String name= null;
try {
name = rw.getString("name");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return bp.getTitle().equals(name);
}).limit(1).collect(Collectors.toList()).get(0);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return new Portal(rw.getInt("id"), "", users.get(0), rw.getString("name"));
} );
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}