Pourquoi est-ce que Java 8 programme compile pas?
Ce programme compile bien dans Java 7 (ou en Java 8 avec -source 7
), mais ne parvient pas à compiler avec Java 8:
interface Iface<T> {}
class Impl implements Iface<Impl> {}
class Acceptor<T extends Iface<T>> {
public Acceptor(T obj) {}
}
public class Main {
public static void main(String[] args) {
Acceptor<?> acceptor = new Acceptor<>(new Impl());
}
}
Résultat:
Main.java:10: error: incompatible types: cannot infer type arguments for Acceptor<>
Acceptor<?> acceptor = new Acceptor<>(new Impl());
^
reason: inference variable T has incompatible bounds
equality constraints: Impl
upper bounds: Iface<CAP#1>,Iface<T>
where T is a type-variable:
T extends Iface<T> declared in class Acceptor
where CAP#1 is a fresh type-variable:
CAP#1 extends Iface<CAP#1> from capture of ?
1 error
En d'autres mots, c'est un en arrière source d'incompatibilité entre Java 7 et 8. Je suis passé par Les incompatibilités entre Java SE 8, et Java SE 7 liste, mais n'a pas trouvé quelque chose qui pourrait convenir à mon problème.
Donc, est-ce un bug?
Environnement:
$ /usr/lib/jvm/java-8-oracle/bin/java -version
java version "1.8.0"
Java(TM) SE Runtime Environment (build 1.8.0-b132)
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b70, mixed mode)
- Connexes: stackoverflow.com/questions/22509488/...
Acceptor<?> acceptor = new Acceptor<Impl>(new Impl())
devrait fonctionner correctement ouAcceptor<Impl> acceptor = new Acceptor<>(new Impl())
.- Sonne comme un bug pour moi. Ressemble au diamant opérateur est confus.
- cette compile très bien pour moi. à l'aide de téléchargements à partir de oracle.com/technetwork/articles/java/lambda-1984522.html en janvier
- Merci de nous montrer votre Java-8-subversion.
- C'est la compilation pour moi. FWIW
(build 1.8.0-b132)
Mac OSX - Sur Windows à l'aide de construire 1.8.0-b132, je peux reproduire votre erreur de compilation.
- Je sais comment le contourner, je veux simplement confirmer si c'est un bug.
- La déclaration semble non-sens de toute façon. Il est étrange que Java 7 permis, et que votre examen du code n'a pas l'attraper.
- Pourquoi?
Vous devez vous connecter pour publier un commentaire.
Merci pour le rapport. Cela ressemble à un bug. Je vais prendre soin d'elle et sans doute ajouter une meilleure réponse une fois que nous aurons plus d'informations sur les raisons de ce qui se passe. J'ai déposé ce bug entrée JDK-8043926, à suivre.
La Java Language Specification changé de façon significative concernant l'inférence de type. Dans JLS7, l'inférence de type a été décrit dans §15.12.2.7 et §15.12.2.8, alors que dans JLS8, il y a tout un chapitre consacré à la Chapitre 18. L'Inférence De Type.
Les règles sont assez complexes, à la fois dans JLS7 et JLS8. Il est difficile de dire les différences, mais de toute évidence il y a des différences, comme cela est évident à partir de la section §18.5.2:
Cependant, le but de ce changement est d'être compatible. Voir le dernier paragraphe de l'article §18.5.2:
Je ne peux pas dire si c'est vrai ou pas. Cependant, il existe quelques variations intéressantes de votre code, et qui ne montrent pas le problème. Par exemple, l'instruction suivante compile sans erreurs:
Dans ce cas, il n'y a pas de type de cible. Cela signifie que le Instance de Classe Créer Expression n'est pas un poly expressions, et les règles pour l'inférence de type sont plus simples. Voir §18.5.2:
C'est également la raison, pourquoi la déclaration suivante œuvres.
Bien qu'il y a un type dans le contexte de l'expression, il ne compte pas comme type de cible. Si un classe création de l'instance de l'expression ne se fait pas dans un expression d'affectation ou un invocation expression, il ne peut pas être un poly expression. Voir §15.9:
De revenir à votre déclaration. La partie pertinente de la JLS8 est de nouveau §18.5.2. Cependant, je ne peux pas vous dire si l'énoncé suivant est correct selon le JLS8, si le compilateur est à droite avec le message d'erreur. Mais au moins, vous avez des solutions de rechange et des pointeurs pour de plus amples informations.
new Acceptor<?>(...)
n'est pas permis dans les deux versions. si rien ses un bug dans le type d'inférence de java7new Acceptor<Impl>(...)
. Autant que je sache, Java 7 inférence de type ne prend jamais le résultat escompté type de compte.List<Integer> list = new ArrayList<>();
il aurait, depuis la précédente déclaration ne contient pas suffisamment d'informations pour faire une inférence.L'inférence de Type a été changé dans Java 8. Maintenant, l'inférence de type en considération à la fois le type de cible et les types de paramètres, tant pour les constructeurs et les méthodes. Considérez les points suivants:
La suite est maintenant ok dans Java 8 (mais pas en Java 7):
Cela, bien sûr, donne une erreur à la fois:
C'est aussi ok dans Java 8:
La suite donne une erreur, mais l'erreur est différente:
Clairement, Java 8 fait le type de système d'inférence plus intelligente. Est-ce à cause des incompatibilités? Généralement, aucun. En raison du type d'effacement, il ne fait pas d'importance ce types ont été déduites, tant que le programme compile. Java 8 compiler tous les Java 7 programmes? Il devrait, mais vous apporté un cas où il ne l'est pas.
Ce qui semble se passer, c'est que Java 8 n'est pas la manipulation des caractères génériques bien. Au lieu de les considérer comme une absence de contrainte, il semble être de les traiter comme une contrainte restrictive qu'il ne peut pas satisfaire. Je ne sais pas si c'est à la suite de la lettre de la JLS, mais je dirais que c'est un bogue au moins dans l'esprit.
Pour info, cela fonctionne (à noter que mon
Acceptor
qui n'ont pas de contraintes de type que le vôtre n'):Notez que votre exemple est l'utilisation d'un générique de type à l'extérieur d'un paramètre de méthode (ce qui est déconseillé), je me demande si la même question va se passer dans la plus typique de code qui utilise le diamant de l'opérateur dans les appels de méthode. (Probablement.)
new Foo<>(..)
certainement les prend en compte les arguments du constructeur pour l'inférence.