我已经看到了一些与此相关的问题,但我需要一个明确的答案。我确实理解lamba表达式运行的上下文以及副作用的概念,但我相信有一种我没有在这里看到的解决方法。
我需要根据他们的性别来映射人物角色列表,但我用来确定他们的性别的方法会返回一个已检查的异常,这根本不是Collectors.grouping所喜欢的。
删除已检查的异常不是一个选项,我需要将其发送到调用我的代码片段的客户端方法。我有什么可以做的吗?
public class Example {
public static void main(String[] args) {
Example example = new Example();
try {
example.runExample();
} catch (MException e) {
//Place where I want to handle the exception
}
}
private void runExample() throws MException{
List<Person> personas = Arrays.asList(new Person("Sergio", "234456789", 35), new Person("Mariana", "123456789", 38));
Map<String, List<Person>> personsBySex = personas.stream().collect(Collectors.groupingBy(persona -> {
try {
return getSex(persona.getSSN());
} catch (MException e) {
}
return null;
//Compiler forces me to return a value, but I don't want to return null.
//I want to throw up the MException to the client method (main)
}));
}
private String getSex(String ssn) throws MException {
// Imagine here is a call to an outside service that would give me the
// sex based on the SSN, but this service could return an exception as
// well
if (ssn.isEmpty())
throw new MException();
return ssn.startsWith("1") ? "Female" : "Male";
}
}
class Person {
private String name, ssn;
private Integer age;
public Person(String name, String ssn, Integer age) {
this.name = name;
this.ssn = ssn;
this.age = age;
}
public String getName() {return name;}
public String getSSN() {return ssn;}
public Integer getAge() {return age;}
}
class MException extends Exception {
}
感谢您的任何想法!
答案 0 :(得分:3)
解决方法可能是使用未经检查的异常包装已检查的异常并将后者从catch
块中抛出。请注意,我们正在保存MException
作为能够进一步使用的原因。
try {
return getSex(persona.getSSN());
} catch (MException e) {
throw new IllegalArgumentException(e); // choose a suitable runtime one
}
另一种方法(相对于前一种方式的修改,我不喜欢)将编写一个Collectors.groupingBy
- 友好的方法来将上述逻辑移动到:
private String getSexFriendly(String ssn) {
try {
return getSex(ssn);
} catch (MException e) {
throw new IllegalArgumentException(e);
}
}
虽然,我们会有一个好看的lambda:
persona -> getSexFriendly(persona.getSSN())
答案 1 :(得分:1)
忘记包装你的例外 - 你可以利用&#34;鬼鬼祟祟的投掷&#39; hack here允许你欺骗编译器认为你的异常没有被检查 - 这会利用Java 8中引入的类型推断规则。
让我们重新创建您的问题:
public Integer toInteger(String string) throws IOException {
throw new IOException("whoopsie!");
}
Stream.of("42")
.collect(Collectors.groupingBy(o -> toInteger(o))); // does not compile
通常,您需要try-catch
异常,就像您一样,但有一个解决方法:
@Test
public void example_1() throws Exception {
Stream.of("42")
.collect(Collectors.groupingBy(unchecked(this::toInteger)));
}
public Integer toInteger(String string) throws IOException {
throw new IOException("whoopsie!");
}
private static <T, R> Function<T, R> unchecked(ThrowingFunction<T, R> f) {
return t -> {
try {
return f.apply(t);
} catch (Throwable thr) {
return ThrowingFunction.sneakyThrow(thr);
}
};
}
public interface ThrowingFunction<T, R> {
R apply(T t) throws Throwable;
@SuppressWarnings("unchecked")
static <T extends Throwable, R> R sneakyThrow(Throwable t) throws T {
throw (T) t;
}
}
首先,您需要创建自己的功能接口来表示可以抛出异常的函数。在这种情况下,ThrowingFunction
。
然后,您可以创建一个实用程序方法,将已检查的lambda重新打包为标准java.util.function
。在这种情况下,unchecked()
。
最后一步是创建一个偷偷抛出异常的方法。在这种情况下,sneakyThrow()
。
实际上,我想我会写一篇关于此的文章。
编辑: 我写道:http://4comprehension.com/sneakily-throwing-exceptions-in-lambda-expressions-in-java/