Nous savons tous que Long étend Number
. Alors pourquoi cela ne compile-t-il pas?
Et comment définir la méthode with
pour que le programme compile sans aucune conversion manuelle?
import Java.util.function.Function;
public class Builder<T> {
static public interface MyInterface {
Number getNumber();
Long getLong();
}
public <F extends Function<T, R>, R> Builder<T> with(F getter, R returnValue) {
return null;//TODO
}
public static void main(String[] args) {
// works:
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// works:
new Builder<MyInterface>().with(MyInterface::getNumber, (Number) 4L);
// works:
new Builder<MyInterface>().<Function<MyInterface, Number>, Number> with(MyInterface::getNumber, 4L);
// works:
new Builder<MyInterface>().with((Function<MyInterface, Number>) MyInterface::getNumber, 4L);
// compilation error: Cannot infer ...
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
// compilation error: Cannot infer ...
new Builder<MyInterface>().with(MyInterface::getNumber, Long.valueOf(4));
// compiles but also involves typecast (and Casting Number to Long is not even safe):
new Builder<MyInterface>().with( myInterface->(Long) myInterface.getNumber(), 4L);
// compiles but also involves manual conversion:
new Builder<MyInterface>().with(myInterface -> myInterface.getNumber().longValue(), 4L);
// compiles (compiler you are kidding me?):
new Builder<MyInterface>().with(castToFunction(MyInterface::getNumber), 4L);
}
static <X, Y> Function<X, Y> castToFunction(Function<X, Y> f) {
return f;
}
}
- Impossible de déduire des arguments de type pour
<F, R> with(F, R)
- Le type de getNumber () du type Builder.MyInterface est Number, ceci est incompatible avec le type de retour du descripteur: Long
Pour le cas d'utilisation, voir: Pourquoi le type de retour lambda n'est-il pas vérifié au moment de la compilation
Le compilateur Java n'est généralement pas bon pour inférer des types génériques ou des caractères génériques multiples/imbriqués. Souvent, je ne peux pas obtenir quelque chose à compiler sans utiliser une fonction d'aide pour capturer ou déduire certains types.
Mais, avez-vous vraiment besoin de capturer le type exact de Function
en tant que F
? Sinon, peut-être que les travaux suivants, et comme vous pouvez le voir, semblent également fonctionner avec des sous-types de Function
.
import Java.util.function.Function;
import Java.util.function.UnaryOperator;
public class Builder<T> {
public interface MyInterface {
Number getNumber();
Long getLong();
}
public <R> Builder<T> with(Function<T, R> getter, R returnValue) {
return null;
}
// example subclass of Function
private static UnaryOperator<String> stringFunc = (s) -> (s + ".");
public static void main(String[] args) {
// works
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
// works
new Builder<String>().with(stringFunc, "s");
}
}
La partie la plus intéressante réside dans la différence entre ces 2 lignes, je pense:
// works:
new Builder<MyInterface>().<Function<MyInterface, Number>, Number> with(MyInterface::getNumber, 4L);
// compilation error: Cannot infer ...
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
Dans le premier cas, le T
est explicitement Number
, donc 4L
est aussi un Number
, pas de problème. Dans le deuxième cas, 4L
est un Long
, donc T
est un Long
, donc votre fonction n'est pas compatible, et Java ne peut pas savoir si vous vouliez dire Number
ou Long
.
Avec la signature suivante:
public <R> Test<T> with(Function<T, ? super R> getter, R returnValue)
tous vos exemples sont compilés, sauf le troisième, qui requiert explicitement que la méthode ait deux variables de type.
La raison pour laquelle votre version ne fonctionne pas est que les références de méthode Java n'ont pas de type spécifique. Au lieu de cela, ils ont le type requis dans le contexte donné. Dans votre cas, R
est supposé être Long
en raison de 4L
, mais le getter ne peut pas avoir le type Function<MyInterface,Long>
car en Java, les types génériques sont invariants dans leurs arguments.