平时我们在编写Java代码中会遇到很多返回空值或抛出异常的方法,例如:
public String test() throws Exception {
File file = new File("....");
if (file.exists()) {
FileInputStream stream = new FileInputStream(file);
//...
return "";
} else {
return null;
}
}
而调用这个方法的代码可能就是:
try {
String str = test();
if (str != null) {
//do something...
} else {
//do something...
}
} catch (Exception e) {
e.printStackTrace();//or ignore
}
这里我们调用test时并不知道返回的是否为空,需要对返回值做一些防护,如果test使用的是运行时异常,那么连是否会抛出异常也不清楚了。
在这种情况下,我们需要表示 test 的返回值为 String|null|Exception 类型,遗憾的是Java并不提供联合类型的语法。我们可以利用 Java 8 的 Optional 类来表示“有或者没有“这样的概念,但是它并没有异常的意思。在这样的场景下,可以参考 Optional 的方式,通过定义 Result 类来实现这样的效果。
Result.empty(); //直接获取空的Result
Result.success(value); //直接获取带值的Result
Result.failure(exception); //使用异常或异常信息来获取错误的Result
Result.of(...); //根据值返回对应的成功、空或失败的Result
Result.from(...); //从一些过程来获取值
result.isEmpty();
result.isPresent();
result.isError();
result.get(); //直接获取值,可能为null
result.getOr(() -> "empty"); //获取值,或者用制定过程的返回值
result.getOrElse("empty"); //获取值,或者默认值
result.getOrThrow(); //获取值,或者抛出异常
这些API包括 filter、map、flatMap、forEach、forEachOrThrow、forEachOrException,具体定义见最后的代码,下面是使用示例:
result.filter(s -> s.length() > 0) //过滤空字符串
.map(s -> "str:" + s) //添加前缀
.forEachOrException((String s) -> {
//处理结果
})
.forEach((Exception e) -> {
//处理异常
})
result.filter(s -> s.length() > 0) //过滤空字符串
.map(s -> "str:" + s) //添加前缀
.forEachOrThrow(s -> {
//处理结果,如果不是Success的Result就抛出异常
});
本类的定义来自于《Java 函数式编程》,并对工作做一些调整,扩展了几个简单的API。定义如下:
public abstract class Result<V> implements Serializable {
private final static Empty empty = new Empty();
private Result() {
}
public abstract boolean isEmpty();
public abstract boolean isError();
public abstract boolean isPresent();
public abstract V get();
public abstract V getOrElse(final V defaultValue);
public abstract V getOr(final Supplier<V> vSupplier);
public abstract V getOrThrow() throws Exception;
public abstract <U> Result<U> map(Function<V, U> f);
public abstract <U> Result<U> flatMap(Function<V, Result<U>> f);
public abstract void forEach(Consumer<V> consumer);
public abstract void forEachOrThrow(Consumer<V> consumer);
public abstract Result<Exception> forEachOrException(Consumer<V> consumer);
public Result<V> orElse(Supplier<Result<V>> defaultValueSupplier) {
return map(v -> this).getOr(defaultValueSupplier);
}
public Result<V> filter(Function<V, Boolean> p) {
return flatMap(v -> p.apply(v)
? success(v)
: failure("Condition not matched"));
}
public Result<V> filter(Function<V, Boolean> p, String message) {
return flatMap(v -> p.apply(v)
? success(v)
: failure(message));
}
public boolean exists(Function<V, Boolean> p) {
return map(p).getOrElse(false);
}
private static class Empty<V> extends Result<V> {
Empty() {
super();
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public boolean isError() {
return false;
}
@Override
public boolean isPresent() {
return false;
}
@Override
public V get() {
return null;
}
@Override
public V getOrElse(V defaultValue) {
return defaultValue;
}
@Override
public V getOr(Supplier<V> vSupplier) {
return vSupplier.get();
}
@Override
public V getOrThrow() throws Exception {
throw new IllegalStateException("empty value!");
}
@Override
public <U> Result<U> map(Function<V, U> f) {
return empty();
}
@Override
public <U> Result<U> flatMap(Function<V, Result<U>> f) {
return empty();
}
@Override
public void forEach(Consumer<V> consumer) {
}
@Override
public void forEachOrThrow(Consumer<V> consumer) {
}
@Override
public Result<Exception> forEachOrException(Consumer<V> consumer) {
return empty();
}
}
private static class Failure<V> extends Empty<V> {
private final RuntimeException exception;
Failure(RuntimeException exception) {
super();
this.exception = exception;
}
Failure(String message) {
super();
this.exception = new IllegalStateException(message);
}
Failure(Exception e) {
super();
this.exception = new IllegalStateException(e.getMessage(), e);
}
@Override
public V getOrElse(V defaultValue) {
return defaultValue;
}
@Override
public V getOr(Supplier<V> vSupplier) {
return vSupplier.get();
}
@Override
public <U> Result<U> map(Function<V, U> f) {
return failure(exception);
}
@Override
public <U> Result<U> flatMap(Function<V, Result<U>> f) {
return failure(exception);
}
@Override
public void forEachOrThrow(Consumer<V> consumer) {
throw exception;
}
@Override
public Result<Exception> forEachOrException(Consumer<V> consumer) {
return success(exception);
}
@Override
public V getOrThrow() throws Exception {
throw exception;
}
@Override
public boolean isError() {
return true;
}
}
private static class Success<V> extends Result<V> {
private final V value;
Success(V value) {
this.value = value;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public boolean isError() {
return false;
}
@Override
public boolean isPresent() {
return true;
}
@Override
public V get() {
return value;
}
@Override
public V getOrElse(V defaultValue) {
return value;
}
@Override
public V getOr(Supplier<V> vSupplier) {
return value;
}
@Override
public V getOrThrow() {
return value;
}
@Override
public <U> Result<U> map(Function<V, U> f) {
try {
return success(f.apply(value));
} catch (Exception e) {
return failure(e);
}
}
@Override
public <U> Result<U> flatMap(Function<V, Result<U>> f) {
try {
return f.apply(value);
} catch (Exception e) {
return failure(e);
}
}
@Override
public void forEach(Consumer<V> consumer) {
consumer.accept(value);
}
@Override
public void forEachOrThrow(Consumer<V> consumer) {
consumer.accept(value);
}
@Override
public Result<Exception> forEachOrException(Consumer<V> consumer) {
consumer.accept(value);
return empty();
}
}
public static <V> Result<V> failure(String message) {
return new Failure<>(message);
}
public static <V> Result<V> failure(RuntimeException e) {
return new Failure<>(e);
}
public static <V> Result<V> failure(Exception e) {
return new Failure<>(e);
}
public static <V> Result<V> success(V value) {
return new Success<>(value);
}
public static <V> Result<V> empty() {
return empty;
}
public static <V> Result<V> of(V value) {
return value != null
? success(value)
: empty();
}
public static <V> Result<V> of(V value, String message) {
return value != null
? success(value)
: failure(message);
}
public static <V> Result<V> of(Function<V, Boolean> p, V value) {
try {
return p.apply(value)
? success(value)
: empty();
} catch (Exception e) {
String errMessage = String.format("Exception while evaluating predicate: %s", e.getMessage());
return failure(new IllegalStateException(errMessage, e));
}
}
public static <V> Result<V> of(Function<V, Boolean> p, V value, String message) {
try {
return p.apply(value)
? success(value)
: failure(String.format(message, value));
} catch (Exception e) {
String errMessage = String.format("Exception while evaluating predicate: %s", String.format(message, value));
return failure(errMessage);
}
}
}