mercredi 4 mars 2015



One of my project is throwing-lambdas; in it I aim to ease the use of potential @FunctionalInterfaces in Streams, whose only "defect" for being used in streams is that they throw checked exceptions (on my part I'd rather call defective the fact that you can't throw checked exceptions from streams but that's another story).


So, for Function<T, R> I define this:



@FunctionalInterface
public interface ThrowingFunction<T, R>
extends Function<T, R>
{
R doApply(T t)
throws Throwable;

default R apply(T t)
{
try {
return doApply(t);
} catch (Error | RuntimeException e) {
throw e;
} catch (Throwable throwable) {
throw new ThrownByLambdaException(throwable);
}
}
}


This allows me to define, for instance:



final ThrowingFunction<Path, Path> = Path::toRealPath;


(why Path::toRealPath... Well, precisely because it has an ellipsis).


Not wanting to stop here I want to be able to write stuff like:



Throwing.function(Path::toRealPath).fallbackTo(Path::toAbsolutePath)


The above NEARLY works... Read on.


I also define this:



public abstract class Chainer<N, T extends N, C extends Chainer<N, T, C>>
{
protected final T throwing;

protected Chainer(final T throwing)
{
this.throwing = throwing;
}

public abstract C orTryWith(T other);

public abstract <E extends RuntimeException> T orThrow(
final Class<E> exclass);

public abstract N fallbackTo(N fallback);

public final <E extends RuntimeException> T as(final Class<E> exclass)
{
return orThrow(exclass);
}
}


And this is the implementation of it for Functions:



public final class ThrowingFunctionChain<T, R>
extends Chainer<Function<T, R>, ThrowingFunction<T, R>, ThrowingFunctionChain<T, R>>
implements ThrowingFunction<T, R>
{
public ThrowingFunctionChain(final ThrowingFunction<T, R> function)
{
super(function);
}

@Override
public R doApply(final T t)
throws Throwable
{
return throwing.doApply(t);
}

@Override
public ThrowingFunctionChain<T, R> orTryWith(
final ThrowingFunction<T, R> other)
{
final ThrowingFunction<T, R> function = t -> {
try {
return throwing.doApply(t);
} catch (Error | RuntimeException e) {
throw e;
} catch (Throwable ignored) {
return other.doApply(t);
}
};

return new ThrowingFunctionChain<>(function);
}

@Override
public <E extends RuntimeException> ThrowingFunction<T, R> orThrow(
final Class<E> exclass)
{

return t -> {
try {
return throwing.doApply(t);
} catch (Error | RuntimeException e) {
throw e;
} catch (Throwable throwable) {
throw ThrowablesFactory.INSTANCE.get(exclass, throwable);
}
};
}

@Override
public Function<T, R> fallbackTo(final Function<T, R> fallback)
{
return t -> {
try {
return doApply(t);
} catch (Error | RuntimeException e) {
throw e;
} catch (Throwable ignored) {
return fallback.apply(t);
}
};
}
}


So far so good (although IDEA fails to recognize the code of orTryWith() as valid, but that's another story).


I also define a utility class called Throwing and the problem lies in the main() of this class which I wrote as a test:



public final class Throwing
{
private Throwing()
{
throw new Error("nice try!");
}

public static <T, R> ThrowingFunctionChain<T, R> function(
final ThrowingFunction<T, R> function)
{
return new ThrowingFunctionChain<>(function);
}

public static void main(final String... args)
{
// FAILS TO COMPILE
final Function<Path, Path> f = function(Path::toRealPath)
.fallbackTo(Path::toAbsolutePath);
}
}


Now, the error message for the code above is:



Error:(29, 48) java: incompatible types: cannot infer type-variable(s) T,R
(argument mismatch; invalid method reference
method toRealPath in interface java.nio.file.Path cannot be applied to given types
required: java.nio.file.LinkOption[]
found: java.lang.Object
reason: varargs mismatch; java.lang.Object cannot be converted to java.nio.file.LinkOption)
Error:(29, 49) java: invalid method reference
non-static method toRealPath(java.nio.file.LinkOption...) cannot be referenced from a static context
Error:(30, 25) java: invalid method reference
non-static method toAbsolutePath() cannot be referenced from a static context


I can't diagnose the exact cause of the error here, but to me it just looks like ellipsis gets in the way; in fact, if I do:



final ThrowingFunctionChain<Path, Path> f = function(Path::toRealPath);

try (
final Stream<Path> stream = Files.list(Paths.get(""));
) {
stream.map(f.fallbackTo(Path::toAbsolutePath))
.forEach(System.out::println);
}


then it compiles: so it means that Stream.map() does acknowledge the result as being a Function...


So why won't Throwing.function(Path::toRealPath).fallbackTo(Path::toAbsolutePath) compile?




Aucun commentaire:

Enregistrer un commentaire