Values
Values
Option
@Test
public void givenValue_whenNullCheckNeeded_thenCorrect() {
Object possibleNullObj = null;
if (possibleNullObj == null) {
possibleNullObj = "someDefaultValue";
}
assertNotNull(possibleNullObj);
}
如果不进行检查,应用程序可能会因为一个简单的
@Test(expected = NullPointerException.class)
public void givenValue_whenNullCheckNeeded_thenCorrect2() {
Object possibleNullObj = null;
assertEquals("somevalue", possibleNullObj.toString());
}
然而,这些检查使代码变得啰嗦,不那么可读,特别是当
@Test
public void givenValue_whenCreatesOption_thenCorrect() {
Option<Object> noneOption = Option.of(null);
Option<Object> someOption = Option.of("val");
assertEquals("None", noneOption.toString());
assertEquals("Some(val)", someOption.toString());
}
请注意,在调用
@Test
public void givenNull_whenCreatesOption_thenCorrect() {
String name = null;
Option<String> nameOption = Option.of(name);
assertEquals("baeldung", nameOption.getOrElse("baeldung"));
}
@Test
public void givenNonNull_whenCreatesOption_thenCorrect() {
String name = "baeldung";
Option<String> nameOption = Option.of(name);
assertEquals("baeldung", nameOption.getOrElse("notbaeldung"));
}
Try
在
@Test(expected = ArithmeticException.class)
public void givenBadCode_whenThrowsException_thenCorrect() {
int i = 1 / 0;
}
如果没有
@Test
public void givenBadCode_whenTryHandles_thenCorrect() {
Try<Integer> result = Try.of(() -> 1 / 0);
assertTrue(result.isFailure());
}
计算是否成功,可以在代码中的任何一点选择检查。在上面的代码段中,我们选择简单地检查成功或失败。我们也可以选择返回一个默认值。
@Test
public void givenBadCode_whenTryHandles_thenCorrect2() {
Try<Integer> computation = Try.of(() -> 1 / 0);
int errorSentinel = result.getOrElse(-1);
assertEquals(-1, errorSentinel);
}
@Test(expected = ArithmeticException.class)
public void givenBadCode_whenTryHandles_thenCorrect3() {
Try<Integer> result = Try.of(() -> 1 / 0);
result.getOrElseThrow(ArithmeticException::new);
}
Try 异常处理
public class VavrTry {
private HttpClient httpClient;
public Try<Response> getResponse() {
return Try.of(httpClient::call);
}
// standard constructors
}
需要注意的是一个返回类型为 Try<Response>
的方法。当一个方法返回这样的结果类型时,我们需要正确处理,并且要记住,这个结果类型可能是
处理成功的结果
让我们写一个测试用例,在Try<Resposne>
对象。因此我们可以调用
@Test
public void givenHttpClient_whenMakeACall_shouldReturnSuccess() {
// given
Integer defaultChainedResult = 1;
String id = "a";
HttpClient httpClient = () -> new Response(id);
// when
Try<Response> response = new VavrTry(httpClient).getResponse();
Integer chainedResult = response
.map(this::actionThatTakesResponse)
.getOrElse(defaultChainedResult);
Stream<String> stream = response.toStream().map(it -> it.id);
// then
assertTrue(!stream.isEmpty());
assertTrue(response.isSuccess());
response.onSuccess(r -> assertEquals(id, r.id));
response.andThen(r -> assertEquals(id, r.id));
assertNotEquals(defaultChainedResult, chainedResult);
}
函数
public int actionThatTakesResponse(Response response) {
return response.id.hashCode();
}
如果
public int actionThatTakesTryResponse(Try<Response> response, int defaultTransformation){
return response.transform(responses -> response.map(it -> it.id.hashCode())
.getOrElse(defaultTransformation));
}
Lazy
@Test
public void givenFunction_whenEvaluatesWithLazy_thenCorrect() {
Lazy<Double> lazy = Lazy.of(Math::random);
assertFalse(lazy.isEvaluated());
double val1 = lazy.get();
assertTrue(lazy.isEvaluated());
double val2 = lazy.get();
assertEquals(val1, val2, 0.1);
}
在上面的例子中,我们正在评估的函数是
我们还可以继续通过再次尝试获取值来确认
处理异常场景
让我们写一个例子,当我们的
@Test
public void givenHttpClientFailure_whenMakeACall_shouldReturnFailure() {
// given
Integer defaultChainedResult = 1;
HttpClient httpClient = () -> {
throw new ClientException("problem");
};
// when
Try<Response> response = new VavrTry(httpClient).getResponse();
Integer chainedResult = response
.map(this::actionThatTakesResponse)
.getOrElse(defaultChainedResult);
Option<Response> optionalResponse = response.toOption();
// then
assertTrue(optionalResponse.isEmpty());
assertTrue(response.isFailure());
response.onFailure(ex -> assertTrue(ex instanceof ClientException));
assertEquals(defaultChainedResult, chainedResult);
}
方法
当我们不想在所有代码库中携带我们的
Pattern Matching
当我们的
@Test
public void givenHttpClientThatFailure_whenMakeACall_shouldReturnFailureAndNotRecover() {
// given
Response defaultResponse = new Response("b");
HttpClient httpClient = () -> {
throw new RuntimeException("critical problem");
};
// when
Try<Response> recovered = new VavrTry(httpClient).getResponse()
.recover(r -> Match(r).of(
Case(instanceOf(ClientException.class), defaultResponse)
));
// then
assertTrue(recovered.isFailure());
只有当
recovered.getOrElseThrow(throwable -> {
throw new RuntimeException(throwable);
});
有些错误是至关重要的,当它们发生时,我们希望通过在调用堆栈中较高的位置抛出异常来明确地发出信号,让调用者决定进一步的异常处理。在这种情况下,像上面的例子一样重新抛出异常是非常有用的。当我们的客户端抛出一个非关键异常时,我们在
@Test
public void givenHttpClientThatFailure_whenMakeACall_shouldReturnFailureAndRecover() {
// given
Response defaultResponse = new Response("b");
HttpClient httpClient = () -> {
throw new ClientException("non critical problem");
};
// when
Try<Response> recovered = new VavrTry(httpClient).getResponse()
.recover(r -> Match(r).of(
Case(instanceOf(ClientException.class), defaultResponse),
Case(instanceOf(IllegalArgumentException.class), defaultResponse)
));
// then
assertTrue(recovered.isSuccess());
}
Either
在函数式编程的世界里,函数值或对象不能被修改(即以正常形式
让我们考虑一个用例,在这个用例中,我们需要创建一个方法,该方法接受一个输入,并根据输入返回一个字符串或一个整数。我们可以用两种方式实现这个方法。要么我们的方法可以返回一个带有代表成功
public static Map<String, Object> computeWithoutEitherUsingMap(int marks) {
Map<String, Object> results = new HashMap<>();
if (marks < 85) {
results.put("FAILURE", "Marks not acceptable");
} else {
results.put("SUCCESS", marks);
}
return results;
}
public static void main(String[] args) {
Map<String, Object> results = computeWithoutEitherUsingMap(8);
String error = (String) results.get("FAILURE");
int marks = (int) results.get("SUCCESS");
}
public static Object[] computeWithoutEitherUsingArray(int marks) {
Object[] results = new Object[2];
if (marks < 85) {
results[0] = "Marks not acceptable";
} else {
results[1] = marks;
}
return results;
}
我们可以看到,这两种方式都需要相当大的工作量,而且最后的效果不是很美观,使用起来也不安全。现在让我们看看如何利用
private static Either<String, Integer> computeWithEither(int marks) {
if (marks < 85) {
return Either.left("Marks not acceptable");
} else {
return Either.right(marks);
}
}
此外,
computeWithEither(80)
.right()
.filter(...)
.map(...)
// ...
按照惯例,
computeWithEither(90).right()
.filter(...)
.map(...)
.getOrElse(Collections::emptyList);