Comment ne pas lancer une exception générique?
J'ai créé une interface "producteur" (à utiliser avec les références de méthode, pour être facilement simulé pour les tests unitaires)
@FunctionalInterface
public interface Factory<R, T, X extends Throwable> {
public R newInstanceFor(T t) throws X;
}
que j’ai créé comme ça, car mon premier cas d’utilisation devait en fait jeter quelques vérifications WhateverException
.
Mais mon deuxième cas d'utilisation n'a pas de X à lancer.
Le mieux que je puisse faire pour rendre le compilateur heureux est:
Factory<SomeResultClass, SomeParameterClass, RuntimeException> factory;
Cela compile, et fait ce dont j'ai besoin, mais toujours moche. Existe-t-il un moyen de conserver cette interface unique sans fournir un X lors de la déclaration d'instances spécifiques?
La seule façon de le faire est de sous-classer - mais je parie que vous le saviez. Pour renforcer mon argument, examinons BinaryOperator
qui étend BiFunction
.
Vous ne pouvez pas faire cela en Java. Le seul moyen est de créer une sous-interface.
public interface DefaultExceptionFactory<R, T>
extends Factory<R, T, RuntimeException>
C’est plutôt une réponse "d’ingénierie sociale": nous plaçons un contrat sur la forme lambda pour qu’elle ne jette rien:
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);
}
};
}
}
L'utilisation est comme ceci, ou quelque chose du genre:
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();
}
}
Inconvénient de cette approche: vous ne pouvez pas vraiment spécifier le contrat dans le type signature, le contrat est alors un détail de mise en œuvre. Si vous voulez avoir cette signature en type, vous aurez besoin d'une deuxième sous-interface.
Vous pouvez définir la méthode comme générique, comme ci-dessous, si cela vous est possible:
@FunctionalInterface
public interface Factory<R, T> {
public <X extends Throwable> R newInstanceFor(T t) throws X;
}
Vous pouvez utiliser l'annotation @SneakyThrows de Project Lombok:
@FunctionalInterface
public interface Factory<R, T> {
@SneakyThrows
R newInstanceFor(T t);
}
Cela vous permet de lever n'importe quelle exception (cochée ou non cochée). Mais lisez la documentation car cette fonctionnalité doit être manipulée avec précaution.