[Java] OptionalやStreamで例外を投げたい


# 目的

- Optional や Stream の map() などで、チェック例外を投げたいことがある（よね？）
- でも標準の仕様だとチェック例外を投げることはできない
- とりあえず、お試しでチェック例外を投げられる Optional もどきと Stream もどきを作って試してみよう（無意味）
- ちなみにタグを見てもらえばわかると思いますが、実用性も皆無だと思います

【2015-11-04 追記】
結構書き直しました。サンプルソースもつけてみた。

# Stream もどき

標準の `java.util.stream.Stream` はインタフェースなので、それを実装しようと思ったけど、少しずつ違うのでよく似た別のクラスを作成します。

まずはインタフェースを追加。例外を投げることのできる Consumer もどきと Function もどき。

```java: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;
}
```

```java: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 もどき。
チェック例外をうまく通せなかったので、かなり強引にスローしています。

```java: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;
	}

    // （あとは省略。実際には実装してます）
}
```

```java: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 クラスで継承できないので、ソースを参考にしてやはり新規に作ってみる。

```java: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));
		}
	}

    // （あとは省略。実際には実装してます）
}
```

呼び出してみます。

```java: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：[サンプルソース](https://gist.github.com/yoshi389111/53dfab68d1a037fc85cf)