Pourquoi Java ne fait-il pas d'inférence de type?

44

Je me suis toujours demandé pourquoi Java ne faisait pas d'inférence de type étant donné que le langage est ce qu'il est et que sa machine virtuelle est très mature. Go's est un exemple de langage avec une excellente inférence et qui réduit le nombre de saisies. Existe-t-il une raison particulière pour que cette fonctionnalité ne fasse pas partie de Java?

Cobie
la source
3
Règles de compatibilité ascendante dans un langage aussi ancien et populaire que Java
Ratchet Freak
9
@ratchetfreak L'inférence de type ne peut-elle pas être ajoutée de manière rétrocompatible? Les programmes plus anciens fourniraient simplement plus d'informations de type que nécessaire.
1
Il peut avoir des effets inattendus lorsqu’il est utilisé avec Type Erasure. docs.oracle.com/javase/tutorial/java/generics/…
Zachary Yates le
4
Notez que Java 8 apportera de nombreuses inférences de types avec sa fonctionnalité Lambda: vous pouvez écrire des lambdas complexes sans mentionner aucun type et tout ce qui est inféré.
Joachim Sauer le
4
L'inférence de type de variable locale arrive en Java: JEP 286: Inférence de type de variable locale
Jimmy Page

Réponses:

60

Techniquement, Java utilise l' inférence de type lors de l'utilisation de génériques. Avec une méthode générique comme

public <T> T foo(T t) {
  return t;
}

Le compilateur analysera et comprendra que lorsque vous écrivez

// String
foo("bar");
// Integer
foo(new Integer(42));

Une chaîne va être renvoyée pour le premier appel et un entier pour le deuxième appel en fonction de ce qui a été entré en tant qu'argument. Vous obtiendrez la vérification appropriée lors de la compilation. De plus, en Java 7, on peut obtenir des déductions de types supplémentaires lors de l’ instanciation de génériques tels que

Map<String, String> foo = new HashMap<>();

Java a la gentillesse de nous indiquer les crochets vierges. Maintenant, pourquoi Java ne prend-il pas en charge l’ inférence de type dans le cadre d’une affectation de variable? À un moment donné, il y avait une RFE pour l'inférence de type dans les déclarations de variable, mais elle était fermée en tant que "ne résoudra pas" car

Les humains bénéficient de la redondance de la déclaration de type de deux manières. Premièrement, le type redondant sert de documentation précieuse - les lecteurs ne doivent pas rechercher la déclaration de getMap () pour savoir quel type il renvoie. Deuxièmement, la redondance permet au programmeur de déclarer le type voulu et de bénéficier ainsi d’un contrôle croisé effectué par le compilateur.

Le contributeur qui a clôturé cette session a également noté que cela ne faisait que ressembler à un "non-java-like", ce avec quoi je suis du même avis. La verbosité de Java peut être à la fois une bénédiction et une malédiction, mais elle fait du langage ce qu’il est.

Bien entendu, ce RFE n’était pas la fin de cette conversation. Au cours de Java 7, cette fonctionnalité a de nouveau été prise en compte , et certaines implémentations de test ont été créées, notamment celle de James Gosling lui-même. Encore une fois, cette fonctionnalité a finalement été abattue.

Avec la sortie de Java 8, nous obtenons maintenant l'inférence de type en tant que partie de lambdas en tant que telle:

List<String> names = Arrays.asList("Tom", "Dick", "Harry");
Collections.sort(names, (first, second) -> first.compareTo(second));

Le compilateur Java est capable d’examiner la méthode Collections#sort(List<T>, Comparator<? super T>), puis l’interface de, Comparator#compare(T o1, T o2)et détermine ainsi firstet seconddevrait Stringpermettre au programmeur de ne pas avoir à reformuler le type dans l’expression lambda.

Serrement
la source
5
First, the redundant type serves as valuable documentation - readers do not have to search for the declaration of getMap() to find out what type it returns- oui, si c'est le cas, HashMap<String, Integer> map = foo.getMap()je suis d'accord - en C #, je ne l'utilise généralement pas var, même si je le pouvais. Mais cet argument ne tient pas si c'est HashMap<String, Integer> map = new HashMap<String, Integer>(). Ceci est une vraie redondance et je ne vois aucun avantage à écrire deux fois le nom du type. Et comment je bénéficierais ici d’une vérification croisée du compilateur, je ne comprends pas du tout.
Konrad Morawski
9
Oui, c'est bien pour les génériques, mais l'équivalent de C # varen Java me manque toujours .
Konrad Morawski
15
Cette histoire (de fromage) me vient à l’esprit;) 9gag.com/gag/2308699/-this-is-how-things-are-done-around-here
Konrad Morawski
6
De plus, le type de retour d'une fonction est souvent évident ou inutile, et même dans les cas où ce n'est pas le cas, votre IDE peut signaler le type en une demi-seconde.
Phoshi
8
Je pense que la citation officielle Humans benefit from the redundancyest la vraie réponse à la question actuelle, car chaque justification que j'ai lue semble être une excuse gratuite, sans fondement et ridicule des concepteurs Java: C # et C ++ ont tous les deux la fonction, les concepteurs C # et C ++ ne sont pas moins compétents que Java, et tout le monde est heureux de l'utiliser. Qu'est-ce qui rendrait les développeurs Java différents des développeurs C # ou C ++? C'est pourquoi je suis d'accord avec @KonradMorawski: "C'est comme ça que les choses se passent ici" semble à nouveau être la vraie raison en coulisse de cela.
paercebal
16

Tout d’abord, l’inférence de type n’a rien à voir avec la maturité du moteur d’exécution, qu’il s’agisse d’un processeur âgé de 30 ans ou d’une VM si récente que les bits sont toujours brillants. tout tourne autour du compilateur.

Cela dit, il est autorisé pour les génériques. La raison pour laquelle il n'est pas autorisé pour les types non génériques semble être dû à la philosophie: rien n'empêche les concepteurs de l'ajouter.

Mise à jour: on dirait que java 10 le supporte - http://openjdk.java.net/jeps/286

jmoreno
la source
5

Autant que je sache, lors de la conception de Java au début des années 90, l'inférence de type n'était pas si populaire parmi les langages traditionnels (mais c'était déjà un concept très connu, par exemple en ML). Donc, je peux imaginer que l’inférence de type n’a probablement pas été prise en charge car Java était destiné aux programmeurs venant de C ++, Pascal ou d’autres langages traditionnels qui ne l’avaient pas (principe de moindre surprise).

En outre, l'un des principes de conception de Java est d'écrire explicitement les choses pour s'assurer que le programmeur et le compilateur ont la même compréhension du code: la duplication d'informations réduit les risques d'erreur. Bien sûr, il peut être une question de goût de savoir si saisir quelques caractères de plus mérite la sécurité supplémentaire apportée, mais telle était la philosophie de conception suivie pour Java: écrire des choses explicitement.

Je ne sais pas si Java obtiendra l'inférence de type à l'avenir, mais IMO serait une grande révolution pour le langage (comme l'a mentionné Glenn Nelson, il a été qualifié de "non-java-like"). nommer Java en faveur d'un nouveau nom.

Si vous souhaitez utiliser un langage JVM avec inférence de type, vous pouvez utiliser Scala.

Giorgio
la source
2

Je peux penser à quelques raisons possibles. La première est que le typage explicite est auto-documenté. Java en fait généralement une priorité par rapport à la concision. Une autre raison pourrait être dans les cas où le type est quelque peu ambigu. Comme quand un type ou un sous-type peut satisfaire une routine. Supposons que vous souhaitiez utiliser une liste, mais que quelqu'un se présente et utilise une méthode exclusive à ArrayList. Le JIT déduirait une ArrayList et continuerait même si vous vouliez une erreur de compilation.

jiggy
la source
8
Comment GridBagLayout gridbag = new GridBagLayout (); ajouter à l'auto-documentation? Sa pure répétition.
Anders Lindén
3
Cela évoque une situation dans laquelle les deux types ne sont pas identiques. Vous pouvez tout aussi bien assigner une instance d'une sous-classe.
Jiggy
Let's say you want to use a List, but someone comes along and uses a method exclusive to ArrayList. The JIT would infer an ArrayList and carry on even if you wanted a compilation error- Je ne comprends pas ce bit. L'inférence de type a lieu au moment d'instancier une variable, sans appeler de méthode. Pourriez-vous peut-être montrer un exemple de ce que vous vouliez dire?
Konrad Morawski
2
@ KonradMorawski: Je suppose qu'il veut dire que si une méthode retourne ArrayList, le type en sera déduit. Si vous voulez traiter un type comme autre chose que le type de retour, vous ne pouvez pas utiliser l'inférence. Je ne comprends pas comment cela pourrait jamais être un problème dans une base de code presque saine d'esprit, cependant.
Phoshi
l'ECE ne jouerait aucun rôle dans l'inférence de type. l'inférence de type est un phénomène de compilation. et si une variable est déclarée comme une référence à une liste, toute tentative d'accès aux membres ArrayList ne saisira pas chèque et vous obtiendrez une erreur de compilation
sara
0

Cela va à l’encontre de la recommandation bien établie de déclarer les variables à l’aide de l’interface la plus générique qui répondra à vos besoins et de les initialiser avec une classe d’implémentation appropriée, comme dans

Collection<String> names = new ArrayList<>();

Efficacement,

var names = new ArrayList<String>();

est rien que le sucre syntaxique pour

ArrayList<String> names = new ArrayList<String>();

Si vous le souhaitez, votre IDE peut le produire à partir de l' new ArrayList<String>()expression en "un clic" (refactor / create local variable), mais rappelez-vous que cela contredit la recommandation "utiliser les interfaces".

Ralf Kleberhoff
la source