web-dev-qa-db-fra.com

Pourquoi Java ne peut-il pas déduire un supertype?

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

19
jukzi

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");

    }
}
0
machfour

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.

0
njzk2

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.

0
Hoopje