GhostCat
GhostCat

Reputation: 140427

How to not throw a generically specified exception?

I created a "producer" interface (to be used with method references, respectively to be easily mocked for unit tests):

@FunctionalInterface
public interface Factory<R, T, X extends Throwable> {
    public R newInstanceFor(T t) throws X;
}

which I created like that, as my first use case actually had to throw some checked WhateverException.

But my second use case doesn't have an X to throw.

The best I could come up with to make the compiler happy is:

Factory<SomeResultClass, SomeParameterClass, RuntimeException> factory;

That compiles, and does what I need, but still ugly. Is there a way to keep that single interface, but not provide an X when declaring specific instances?

Upvotes: 19

Views: 1861

Answers (6)

foundationer
foundationer

Reputation: 85

You can use Project Lombok's @SneakyThrows annotation:

@FunctionalInterface
public interface Factory<R, T> {

    @SneakyThrows
    R newInstanceFor(T t);
}

This allows you to throw any exception (checked or unchecked). But read the documentation because this feature must be handled with care.

Upvotes: 0

M. Prokhorov
M. Prokhorov

Reputation: 3993

This is more of a "social engineering" answer: we place a contract on the lambda form that it doesn't throw anything:

public interface Factory<T, R, X> {

    public R newInstanceFor(T arg) throws X;

    public static Factory<R, U, AssertionError> neverThrows(Factory<U, V, ?> input) {
        return u -> {
            try {
                return input.newInstanceFor(u);
            }
            catch(Throwable t) {
                throw new AssertionError("Broken contract: exception thrown", t);
            }
        };
    }
}

Usage is like this, or something along the lines of:

class MyClass {
    Factory<MyInput, MyOtherClass, AssertionError> factory;

    MyClass(Factory<MyInput, MyOtherClass, ?> factory) {
        this.factory = Factory.neverThrows(factory);
    }

    public void do() {
      factory.newInstanceFor(new MyInput()).do();
    }
}

Downside of this approach: you can't really specify the contract in the type signature, the contract is then an implementation detail. If you want to have this in type signature, you will need a second sub-interface.

Upvotes: 5

Beno
Beno

Reputation: 987

You can define the method as generic like below code, if it is possible for you:

@FunctionalInterface
public interface Factory<R, T> {
    public <X extends Throwable> R newInstanceFor(T t) throws X;
}

Upvotes: 3

Yogesh Pandit
Yogesh Pandit

Reputation: 1

Do you have to make the exception generic? Why not define the interface as

@FunctionalInterface
public interface Factory<R, T> {
    public R newInstanceFor(T t) throws Throwable;
}

You can always catch your exception and check the type if you need in your calling function.

Upvotes: -3

Eugene
Eugene

Reputation: 120848

The only way to do it is subclassing - but I bet you knew that. To make my argument stronger, look at BinaryOperator that extends BiFunction.

Upvotes: 11

Leo Aso
Leo Aso

Reputation: 12463

You cannot do that in Java. The only way is to create a sub interface.

public interface DefaultExceptionFactory<R, T>
        extends Factory<R, T, RuntimeException>

Upvotes: 12

Related Questions