J'ai remarqué une spécification du langage Java légèrement contre-intuitive, je vais donc en prendre note. L'environnement de vérification est OpenJDK 11 (build 28) pour Windows.
public static <T> void hoge(@NonNull T value){
Class<? extends T> type = value.getClass(); //Impossible de compiler
//Je veux travailler avec le type
}
Puisque la valeur devrait être «T» ou sa sous-classe, cela semble être correct à première vue, mais ce code ne peut pas être compilé.
javac
GenericGetClass.java:3:Erreur:Type incompatible: Class<CAP#1>Classe<? extends T>Ne peut pas être converti en:
Class<? extends T> type = value.getClass();
^
Si T est une variable de type:
Méthode<T>hoge(T)T étend l'objet déclaré dans
CAP#Si 1 est une nouvelle variable de type:
?CAP de la capture de l'objet étend#1 extends Object
1 erreur
Je ne comprends pas le message d'erreur ...
Je vais en essayer.
//Bien sûr OK
public static void hoge(CharSequence value){
Class<? extends CharSequence> type = value.getClass();
}
//C'est aussi OK
public static <T extends CharSequence> void fuga(T value){
Class<? extends CharSequence> type = value.getClass();
}
//Cela ne peut pas être compilé
public static <T extends CharSequence> void piyo(T value){
Class<? extends T> type = value.getClass();
}
Il semble qu'une erreur se produira si la variable de type «T» est utilisée pour le type à attribuer. Après avoir essayé jusqu'à présent, je me suis souvenu de l'histoire selon laquelle la gestion du type de ʻObject # getClass () `est spéciale, alors je l'ai vérifiée à nouveau.
getClass ()
Selon les Spécifications de l'API
Le type de résultat réel est Classe<? extends |X|>est. ici,|X|Élimination du type statique de l'expression pour laquelle getClass est appelé.
En premier lieu, la classe ʻObject est définie comme
Public final Class <?> GetClass () `, donc
CharSequence cs = ...;
Class<?> type = cs.getClass()
C'est possible, mais si vous le traitez comme une méthode normale
Class<? extends CharSequence> type = cs.getClass()
Il ne sera pas possible de remplacer comme. Par conséquent, il est traité spécialement en utilisant le langage, et dans ce cas, le type de getClass ()
est considéré comme Class <? Extends CharSequence>
. Plus précisément, la limite de type est déterminée par le type de la variable au moment de la compilation (= "type statique").
Le problème ici est qu'il "efface le type statique". "Erase" semble être une traduction subtile, mais Spécification du langage §4.6 C'est probablement l'effacement de. Il semble indiquer List
pour List <String>
, CharSequence
pour T
whenclass Hoge <T extend CharSequence>
, et ainsi de suite.
public static <T extends CharSequence> void fuga(T value){
Class<? extends CharSequence> type = value.getClass(); // OK
}
Dans le cas de, puisque value
est de type T
et que sa spécification ʻextends [^ 1] est
CharSequence, la gomme de
T est
CharSequence, qui est la valeur de retour de
getClass ()`. Le type est «? Etend CharSequence». Ce n'est pas nécessairement «T» ou ses sous-classes, il ne peut donc pas être attribué à «Classe <? Etend T>».
[^ 1]: Il est relié le plus à gauche en anglais, mais y a-t-il une bonne traduction en japonais?
Dans le premier exemple
public static <T> void hoge(@NonNull T value){
Class<? extends T> type = value.getClass(); //Impossible de compiler
}
Puisque la gomme de T
est ʻObject, le type de
getClass () ʻest? Extends Object
.
À partir de là, la supposition de l'auteur est tout à fait incluse, mais la raison pour laquelle la gomme est spécifiée au lieu du type générique en premier lieu est que cela se produit avec le type générique.
ArrayList<String> list = new ArrayList<>();
Class<? extends ArrayList<String>> type = list.getClass(); //En fait une erreur de compilation
J'ai un mauvais pressentiment.
Une instance de la classe Class
n'a pas d'informations de type détaillées en raison de son mécanisme [^ 2], donc les objets de type Class <ArrayList <String >>
et du typeClass <ArrayList <Integer >>
sont en fait Ce sera une instance de la même Class <ArrayList>
. La méthode de Class <ArrayList>
ne peut pas savoir à quoi la variable de type T
de ʻArrayList
Autrement dit, les variables de type «Class <ArrayList
[^ 2]: Puisqu'il existe un objet de classe unique pour chaque classe (vous pouvez donc comparer avec ==
)
ArrayList<Integer> another = new ArrayList<>();
another.add(123);
ArrayList<String> casted = type.cast(another); //Au moment de l'exécution, c'est juste un cast vers ArrayList, donc ça passe
// ...
String str = casted.get(0);//Erreur d'exécution ici?
Il est probable que vous obteniez une ClassCastException
à un emplacement inattendu, même si vous l'avez écrite et compilée sans utiliser le type brut globalement.
Je comprends pourquoi vous ne pouvez pas utiliser de types génériques. Cela dit, je pensais que je n'aurais pas à supprimer la variable de type, mais c'est la même chose si la variable de type «T» est affectée à «List
J'ai remarqué ce comportement
@SuppressWarnings("unchecked")
public static <K extends Enum<K>> EnumMap<K,String> of(@NonNull K key, String value){
Class<K> type = (Class<K>) key.getClass(); //Erreur de compilation si non cast
EnumMap<K,String> map = new EnumMap<>(type); //Passer une constante Enum étendue entraînera ici une erreur d'exécution
map.put(key, value);
return map;
}
C'était quand j'écrivais du code comme [^ 4]. Dans ce cas, ʻenumest
final, donc j'ai pensé que c'était correct, mais si vous y réfléchissez bien, si vous définissez une méthode pour chaque constante de ʻenum
, ce sera une classe distincte, donc elle était sortie.
[^ 4]: En fait, je faisais quelque chose comme une implémentation originale d'EnumMap.
J'ai écrit un article sur Qiita pour la première fois, mais si vous y réfléchissez et l'écrivez, cela vous aidera certainement à organiser vos connaissances. Veuillez signaler tous les points qui ne peuvent être atteints.
Recommended Posts