Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does a method reference to ctor that "throws" ... throw as well?

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 )
like image 355
GhostCat Avatar asked Nov 22 '17 12:11

GhostCat


People also ask

How do you know if a method is throwing exception?

The calculate method should check for an exception and if there is no exception, return the calculated value to the main function i.e. v1+v2 or v1-v2; Else if an exception exists then it should print the error statement and the value that is returned from the calculate method to the main method should be 0.0(Not ...

Why throws declared with method signature?

Checked exceptions are always declared as thrown in the method signature. The signature lets the method's caller know that an exception may occur as a consequence of the call. If the exception does get thrown, the caller must be prepared to do something about it.

How do you call a method that throws an exception in Java?

The throw keyword in Java is used to explicitly throw an exception from a method or any block of code. We can throw either checked or unchecked exception. The throw keyword is mainly used to throw custom exceptions.

Should constructor throw exception?

You absolutely should throw an exception from a constructor if you're unable to create a valid object. This allows you to provide proper invariants in your class. In practice, you may have to be very careful.


1 Answers

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.

like image 107
luk2302 Avatar answered Oct 01 '22 13:10

luk2302