Why no Streamable<T> extends Iterable<T> ?
Timo Kinnunen
timo.kinnunen at gmail.com
Thu Dec 19 14:02:55 PST 2013
A rhetoric question; I’ve already read the reasoning. Instead of trying to come up with such a Streamable<T> to prove everyone wrong I’ve written some code which reads JarEntries from a JarFile and in doing so I ended up with Streamed<T>. Streamed is a utility class which tries to handle resources that need to be closed but can throw exceptions without trivially ignoring them, without trivially wrapping them in new unchecked exceptions to be ignored later, but tackling the problem head-on.
It turns out Streamed, full name Streamed<T, A extends AutoCloseable, E extends Exception> implements AutoCloseable, Iterable<T> isn’t as trivial as I’d have liked. I’m not sure it’s even correct.. Does this look reasonable??
---- some exception-aware functional interfaces, a Supplier analogue ----
@FunctionalInterface public interface Make<R, E extends Exception> { R create() throws E;}
---- a Function analogue ----
@FunctionalInterface public interface Change<T, R, E extends Exception> { R evolve(T t) throws E; }
---- and a Consumer analogue ----
@FunctionalInterface public interface Use<T, E extends Exception> { void consume(T t) throws E; }
---- a wrapper exception that can escape :-( ----
public final class OnCloseException extends RuntimeException {
final Exception original;
final Class<?> originalClass;
public <E extends Exception> OnCloseException(E e, Class<E> classOfE) {
super(e);
this.original = e;
this.originalClass = classOfE;
}
public <E extends Exception> void rethrow(Class<E> classOfE) throws E {
if(originalClass == classOfE) {
throw classOfE.cast(original);
}
throw this;
}
@Override
public synchronized Throwable fillInStackTrace() {
return this;
}
}
---- the utility class itself ----
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Iterator;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Stream;
public final class Streamed<T, A extends AutoCloseable, E extends Exception> implements AutoCloseable, Iterable<T> {
private final A resource;
public final Stream<T> stream;
private final Use<Runnable, E> unwrapper;
private final Runnable closeHandler;
private Streamed(Make<A, E> reserver, Change<A, Stream<T>, E> opener, Use<A, ? extends RuntimeException> wrapper, Use<Runnable, E> unwrapper) throws E {
this.resource = reserver.create();
this.unwrapper = unwrapper;
this.closeHandler = () -> wrapper.consume(resource);
this.stream = opener.evolve(resource).onClose(closeHandler);
}
@Override
public void close() throws E {
unwrapper.consume(closeHandler);
}
@Override
public Iterator<T> iterator() {
return stream.iterator();
}
public static Streamed<JarEntry, JarFile, IOException> fromJar(Path file) throws IOException {
Make<JarFile, IOException> reserver = () -> new JarFile(file.toFile());
Change<JarFile, Stream<JarEntry>, IOException> opener = (JarFile jar) -> jar.stream();
Use<JarFile, IOException> releaser = (JarFile jar1) -> jar1.close();
Use<JarFile, OnCloseException> wrapper = wrapper(releaser);
Use<Runnable, IOException> unwrapper = unwrapper();
return new Streamed<>(reserver, opener, wrapper, unwrapper);
}
public static Streamed<String, Stream<String>, IOException> fromLinesInFile(Path file) throws IOException {
Make<Stream<String>, IOException> reserver = () -> Files.lines(file);
Change<Stream<String>, Stream<String>, IOException> opener = (Stream<String> stream) -> stream;
Use<Stream<String>, IOException> releaser = (Stream<String> stream) -> stream.close();
Use<Stream<String>, OnCloseException> wrapper = wrapper(releaser);
Use<Runnable, IOException> unwrapper = unwrapper();
return new Streamed<>(reserver, opener, wrapper, unwrapper);
}
private static <T> Use<T, OnCloseException> wrapper(Use<T, IOException> releaser) {
return (T jar) -> {
try {
releaser.consume(jar);
} catch(IOException e) {
throw new OnCloseException(e, IOException.class);
}
};
}
private static Use<Runnable, IOException> unwrapper() {
return (Runnable runnable) -> {
try {
runnable.run();
} catch(OnCloseException e) {
e.rethrow(IOException.class);
}
};
}
}
---- if you’ve read this far, some closing thoughts ----
I could ask for some help from the library to make all that better, but rather than that..
Here’s why I think there is no Streamable<T> extends Iterable<T>: it’s because Streams are designed wrong. They start by allocating a source of items of T, called Stream<T>. That’s step 1. Then, the operations to be taken on the items T are defined, the result of this step is also called a Stream<T> because it’s irredeemably intertwined with the source of the items. This is step 2. In step 3 the operations are committed and results, if any, observed. If instead step 1 and step 2 switched places then providing a Streamable interface should be trivial.
Sent from Windows Mail
More information about the lambda-dev
mailing list