目的
- Optional や Stream の map() などで、チェック例外を投げたいことがある(よね?)
- でも標準の仕様だとチェック例外を投げることはできない
- とりあえず、お試しでチェック例外を投げられる Optional もどきと Stream もどきを作って試してみよう(無意味)
- ちなみにタグを見てもらえばわかると思いますが、実用性も皆無だと思います
【2015-11-04 追記】
結構書き直しました。サンプルソースもつけてみた。
Stream もどき
標準の java.util.stream.Stream
はインタフェースなので、それを実装しようと思ったけど、少しずつ違うのでよく似た別のクラスを作成します。
まずはインタフェースを追加。例外を投げることのできる Consumer もどきと Function もどき。
Consumer2.java
import java.util.function.Consumer;
@FunctionalInterface
public interface Consumer2<T, E extends Exception> extends Consumer<T> {
@Override
default void accept(T t) {
try {
accept0(t);
} catch (Exception e) {
Stream2.sneakyThrows(e);
}
}
void accept0(T t) throws E;
}
Function2.java
import java.util.function.Function;
@FunctionalInterface
public interface Function2<T, R, E extends Exception> extends Function<T, R> {
@Override
default R apply(T t) {
try {
return apply0(t);
} catch (Exception e) {
Stream2.sneakyThrows(e);
// NOTREACHED
throw new AssertionError();
}
}
R apply0(T t) throws E;
}
Stream もどき。
チェック例外をうまく通せなかったので、かなり強引にスローしています。
Stream2.java
// import 文は省略
public class Stream2<E> implements AutoCloseable {
/** 元となる Stream */
private final Stream<E> base;
/** Stream から Stream2 を生成 */
public static <E> Stream2<E> of(Stream<E> base) {
return new Stream2<E>(base);
}
/** コンストラクタ */
private Stream2(Stream<E> base) {
this.base = Objects.requireNonNull(base);
}
// ...
/** 変換 */
public <R, T extends Exception> Stream2<R> map(Function2<? super E, ? extends R, T> mapper) throws T {
return of(base.map(mapper));
}
/** 処理 */
public <T extends Exception> void forEach(Consumer2<? super E, T> action) throws T {
base.forEach(action);
}
/** 無理やり例外を投げる */
public static <E extends Exception> void sneakyThrows(Exception exception) throws E {
@SuppressWarnings("unchecked")
E e = (E) exception;
throw e;
}
// (あとは省略。実際には実装してます)
}
Stream2Test.java
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
public class Stream2Test {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alfa", "Bravo", "Charlie");
// チェック例外を投げないならは、try - catch は不要
Stream2<String> stream1 = Stream2.of(names.stream());
stream1.forEach(name -> {
System.out.println(name);
});
// チェック例外を外側でキャッチ
try {
Stream2<String> stream2 = Stream2.of(names.stream());
stream2.forEach(name -> {
method(name);
});
} catch (IOException e) {
e.printStackTrace();
}
}
private static String method(String name) throws IOException {
if (name.startsWith("C")) {
throw new IOException(name);
} else {
System.out.println(name);
return name;
}
}
}
実行結果。
Alfa
Bravo
Charlie
Alfa
Bravo
java.io.IOException: Charlie
at Stream2Test.method(Stream2Test.java:30)
at Stream2Test.lambda$1(Stream2Test.java:20)
at Consumer2.accept(Consumer2.java:9)
at java.util.Spliterators$ArraySpliterator.forEachRemaining(Unknown Source)
at java.util.stream.ReferencePipeline$Head.forEach(Unknown Source)
at Stream2.forEach(Stream2.java:99)
at Stream2Test.main(Stream2Test.java:19)
Optional もどき
標準の java.util.Optional
は final クラスで継承できないので、ソースを参考にしてやはり新規に作ってみる。
Optional2.java
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;
/** 例外を投げられる Optional もどき */
public final class Optional2<T> {
/** 空の Optional2 */
private static final Optional2<?> EMPTY = new Optional2<>(null);
/** 値 */
private final T value;
/** コンストラクタ */
private Optional2(T value) {
this.value = value;
}
/** null でない Optional2 を生成 */
public static <T> Optional2<T> of(T value) {
Objects.requireNonNull(value);
return new Optional2<>(value);
}
/** 値がある場合に変換を行う */
public <U, E extends Exception> Optional2<U> map(Function2<? super T, ? extends U, E> mapper) throws E {
Objects.requireNonNull(mapper);
if (!isPresent()) {
return empty();
} else {
return Optional2.ofNullable(mapper.apply(value));
}
}
// (あとは省略。実際には実装してます)
}
呼び出してみます。
Optional2Test.java
import java.io.IOException;
public class Optional2Test {
public static void main(String[] args) {
// チェック例外を投げないならは、try - catch は不要
String s1 = Optional2.of("Charlie")
.map(s -> s + s)
.orElse("null");
System.out.println(s1);
// チェック例外を外側でキャッチ
try {
Optional2<String> opt = Optional2.of("Charlie");
String s2 = opt.map(Optional2Test::method)
.orElse("null");
System.out.println(s2);
} catch (IOException e) {
e.printStackTrace();
}
}
private static String method(String name) throws IOException {
if (name.startsWith("C")) {
// チェック例外を投げる
throw new IOException(name);
} else {
System.out.println(name);
return name;
}
}
}
実行結果。
CharlieCharlie
java.io.IOException: Charlie
at Optional2Test.method(Optional2Test.java:29)
at Function2.apply(Function2.java:9)
at Optional2.map(Optional2.java:84)
at Optional2Test.main(Optional2Test.java:16)
サンプル
gist:サンプルソース