20

I am looking for an elegant way to create a factory for dependency injection. In my case, the factory simply has to call a one-argument constructor. I found this answer outlining how to use a Function<ParamType, ClassToNew> for such purposes.

But my problem is: in my case, my ctor declares to throw some checked exception.

What I don't get: creating that Function using a method reference to that constructor doesn't work. As in:

import java.util.function.Function;

public class Mcve {        
    public Mcve(String s) throws Exception {
        // whatever
    }        
    public static void main(String[] args) {
        Function<String, Mcve> mcveFactory = Mcve::new;
    }
}

tells me about "Unhandled exception: java.lang.Exception" for Mcve::new. Although this code is not invoking the constructor.

Two questions:

  • why that error? The above code does not invoke the ctor (yet)?
  • are there any elegant ways to solve this puzzle? ( simply adding throws Exception to my main() does not help )
GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • 3
    Actually, when I try to compile this it does not say "unhandled exception", but "incompatible thrown types Exception in method reference", which makes more sense. – tobias_k Nov 22 '17 at 14:49
  • 2
    Also, highly related: [Java 8 Lambda function that throws exception?](https://stackoverflow.com/q/18198176/1639625) – tobias_k Nov 22 '17 at 14:50
  • @tobias_k To be precise: that error message is what IntelliJ is telling me. (Unhandled exception ... ) – GhostCat Nov 22 '17 at 14:50
  • "that error message" Which one, the one from your question or the one from my comment? – tobias_k Nov 22 '17 at 14:51
  • @tobias_k The one message I have in my question and that you quoted in your comment? Or are there others? ;-) – GhostCat Nov 22 '17 at 14:52

3 Answers3

24

You need to provide a custom interface ThrowingFunction which has one method that throws Exception.

public interface ThrowingFunction<ParameterType, ReturnType> {
    ReturnType invoke(ParameterType p) throws Exception;
}

public class Mcve {
    public Mcve(String s) throws Exception {
        // whatever
    }
    public static void main(String[] args) {
        ThrowingFunction<String, Mcve> mcveFactory = Mcve::new;
    }
}

Using this approach results in calling mcveFactory.invoke("lalala"); forcing you to handle the exception thrown by the constructor.

Reason for the error is that the actual function reference you want to store (not 100% sure about the terminology) throws an exception and therefore the types simply do not match up. If you could store Mcve::new inside a function then whoever calls the function no longer knows an Exception can be thrown. What would then happen if the exception would actually be thrown? Both throwing the exception and discarding it do not work.


Alternative: if you need to actually retrieve a Function<String, Mcve> in the end then you need to write a function (or lambda) that invokes the constructor, catches the exception and either discards it or rethrows it wrapped inside a unchecked RuntimeException.

public class Mcve {
    public Mcve(String s) throws Exception {
        // whatever
    }

    public static void main(String[] args) {
        Function<String, Mcve> mcveFactory = parameter -> {
            try {
                return new Mcve(parameter);
            } catch (Exception e) {
                throw new RuntimeException(e); // or ignore
            }
        };
    }
}

I would argue that the error message itself is at least a bit misleading since you normally see it when actually invoking the method. I can certainly understand the confusion resulting in the first sub-question. It would be clearer (sadly not possible) to state something like

Incompatible types Function<String,Mcve> vs. Function<String,Mcve> throws Exception.

luk2302
  • 55,258
  • 23
  • 97
  • 137
2

I had to do that just recently... If you can change the class definition, you could use the infamous sneaky throws way of doing things:

static class OneArg {

    private final String some;

    @SuppressWarnings("unchecked")
    public <E extends Exception> OneArg(String some) throws E {
        try {
            this.some = some;
            // something that might throw an Exception... 
        } catch (Exception e) {
            throw (E) e;
        }
    }

    public String getSome() {
        return some;
    }
}

Function<String, OneArg> mcveFactory = OneArg::new;
Eugene
  • 117,005
  • 15
  • 201
  • 306
  • 2
    I only knew [`@SneakyThrows`](https://projectlombok.org/features/SneakyThrows) from lombok, had no idea you could do it with plain java, that looks incredibly hacky but interesting. +1 for the sake of learning something new. – luk2302 Nov 22 '17 at 14:04
  • Nice idea, but not really: your hack basically "invalidates" the throws clause. So the code still says "throws SomeCheckedExceptions" but code calling the ctor isn't forced to try/catch any more. In that sense, this goes along the lines that Y_xxx proposed initially: to simply drop the "throws" clause somehow. – GhostCat Nov 22 '17 at 14:41
  • @GhostCat I've added one more option - in case you mind and disagree with it - I might delete it... – Eugene Nov 23 '17 at 09:21
  • I’d have split the code into the constructor with the `throws Exception` clause and a helper method doing the sneaky throw with the unchecked cast for clarity, but anyway, that’s the working solution; afaik the only one remaining that is not depreated/defunc in the recent Java version. Well, it would generate a warning if it wasn’t suppressed. – Holger Dec 14 '17 at 17:09
1

I've been thinking about this for a while and indeed - if you want to have a Function that declares clearly your intention, I think that you need to have a Function that would extend the java.util.Function, something like this:

@FunctionalInterface
public interface ThrowingFunction<T, R> extends Function<T, R> {

    R applyWithExc(T t) throws Exception;

    @Override
    default R apply(T t) {
        try {
            return applyWithExc(t);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

You can btw choose which method you call when you define your constructor reference - the one that would throw an Exception and one that would silently wrap it with a RuntimeException.

Eugene
  • 117,005
  • 15
  • 201
  • 306
  • One other approach, worth an upvote. But I think it isn't too practical in the real world - as you would need a special interface for each different checked exception you are dealing with. And then: in the end the whole function thing is more of a "byproduct" - in the end, it is about writing down a *factory*. A factory that has type "Function" and that you call with "apply" to create a new instance ... doesn't exactly fit my rules for clear, speaking names ;-) – GhostCat Nov 23 '17 at 10:23