Ich habe eine etwas eingängige Java-Sprachspezifikation bemerkt, daher werde ich sie notieren. Die Überprüfungsumgebung ist OpenJDK 11 (Build 28) für Windows.
public static <T> void hoge(@NonNull T value){
Class<? extends T> type = value.getClass(); //Kann nicht kompiliert werden
//Ich möchte mit Typ arbeiten
}
Da der Wert "T" oder seine Unterklasse sein sollte, scheint er auf den ersten Blick in Ordnung zu sein, aber dieser Code kann nicht kompiliert werden.
javac
GenericGetClass.java:3:Error:Inkompatibler Typ: Class<CAP#1>Klasse<? extends T>Kann nicht konvertiert werden:
Class<? extends T> type = value.getClass();
^
Wenn T eine Typvariable ist:
Methode<T>hoge(T)T erweitert das in deklarierte Objekt
CAP#Wenn 1 eine neue Typvariable ist:
?CAP von der Erfassung des erweiterten Objekts#1 extends Object
1 Fehler
Ich verstehe die Fehlermeldung nicht ...
Ich werde es versuchen.
//Natürlich OK
public static void hoge(CharSequence value){
Class<? extends CharSequence> type = value.getClass();
}
//Dies ist auch in Ordnung
public static <T extends CharSequence> void fuga(T value){
Class<? extends CharSequence> type = value.getClass();
}
//Dies kann nicht kompiliert werden
public static <T extends CharSequence> void piyo(T value){
Class<? extends T> type = value.getClass();
}
Es scheint, dass ein Fehler auftritt, wenn die Typvariable "T" für den zuzuweisenden Typ verwendet wird. Nachdem ich es bisher versucht hatte, erinnerte ich mich an die Geschichte, dass die Behandlung des Typs "Object # getClass ()" etwas Besonderes ist, also überprüfte ich es erneut.
getClass ()
Gemäß den API-Spezifikationen
Der tatsächliche Ergebnistyp ist Klasse<? extends |X|>ist. Hier,|X|Ist die Eliminierung des statischen Typs des Ausdrucks, für den getClass aufgerufen wird.
In erster Linie wird die Klasse "Object" als "public final Class <?> GetClass ()" definiert
CharSequence cs = ...;
Class<?> type = cs.getClass()
Ist möglich, aber wenn Sie es wie eine normale Methode behandeln
Class<? extends CharSequence> type = cs.getClass()
Es wird nicht möglich sein, wie zu ersetzen. Daher wird es speziell unter Verwendung der Sprache behandelt, und in diesem Fall wird der Typ von "getClass ()" als "Class <? Extends Char Sequence>" angesehen. Insbesondere wird die Typbegrenzung durch den Typ der Variablen zur Kompilierungszeit bestimmt (= "statischer Typ").
Das Problem hierbei ist, dass der statische Typ "gelöscht" wird. "Löschen" scheint eine subtile Übersetzung zu sein, aber Sprachspezifikation §4.6 Es ist wahrscheinlich die Löschung von. Es scheint "List" für "List
public static <T extends CharSequence> void fuga(T value){
Class<? extends CharSequence> type = value.getClass(); // OK
}
Im Fall von, da "Wert" vom Typ "T" ist und seine "erweiterte" Spezifikation [^ 1] "CharSequence" ist, ist der Radierer von "T" "CharSequence", was der Rückgabewert von "getClass ()" ist. Der Typ ist "? Erweitert CharSequence". Dies ist nicht unbedingt "T" oder seine Unterklassen, daher können Sie es nicht "Klasse <? Erweitert T>" zuweisen.
[^ 1]: Es ist auf Englisch ganz links gebunden, aber gibt es eine gute japanische Übersetzung?
Im ersten Beispiel
public static <T> void hoge(@NonNull T value){
Class<? extends T> type = value.getClass(); //Kann nicht kompiliert werden
}
Da der Radierer von "T" "Objekt" ist, ist der Typ von "getClass ()" "? Erweitert Objekt".
Von hier aus ist die Vermutung des Autors durchaus enthalten, aber der Grund, warum der Radiergummi anstelle des generischen Typs angegeben wird, ist, dass dies mit dem generischen Typ geschieht.
ArrayList<String> list = new ArrayList<>();
Class<? extends ArrayList<String>> type = list.getClass(); //Eigentlich ein Kompilierungsfehler
Ich habe ein schlechtes Gefühl.
Da Instanzen der Klasse "Class" aufgrund des Mechanismus [^ 2] keine detaillierten Typinformationen haben, sind Objekte vom Typ "Class <ArrayList
Das heißt, Variablen vom Typ "Class <ArrayList
[^ 2]: Da es für jede Klasse ein eindeutiges Klassenobjekt gibt (damit Sie es mit ==
vergleichen können)
ArrayList<Integer> another = new ArrayList<>();
another.add(123);
ArrayList<String> casted = type.cast(another); //Zur Laufzeit ist es nur eine Umwandlung in ArrayList, also geht es vorbei
// ...
String str = casted.get(0);//Laufzeitfehler hier?
Es scheint, dass "ClassCastException" an einem unerwarteten Ort auftritt, obwohl es geschrieben und kompiliert wird, ohne den Rohtyp als Ganzes zu verwenden.
Ich verstehe, warum Sie keine generischen Typen verwenden können. Das heißt, ich dachte, ich müsste die Typvariable nicht löschen, aber es ist dasselbe, wenn die Typvariable T
im Aufrufer der Liste
Ich habe dieses Verhalten bemerkt
@SuppressWarnings("unchecked")
public static <K extends Enum<K>> EnumMap<K,String> of(@NonNull K key, String value){
Class<K> type = (Class<K>) key.getClass(); //Kompilierungsfehler, wenn nicht umgewandelt
EnumMap<K,String> map = new EnumMap<>(type); //Das Übergeben einer erweiterten Enum-Konstante führt hier zu einem Laufzeitfehler
map.put(key, value);
return map;
}
Es war, als ich Code wie [^ 4] schrieb. In diesem Fall ist "enum" "final", also dachte ich, dass es in Ordnung ist, aber wenn Sie sorgfältig darüber nachdenken, wenn Sie eine Methode für jede Konstante von "enum" definieren, wird es eine separate Klasse sein, also war es out.
[^ 4]: Eigentlich habe ich so etwas wie eine ursprüngliche Implementierung von EnumMap gemacht.
Ich habe zum ersten Mal einen Artikel über Qiita geschrieben, aber wenn Sie darüber nachdenken und ihn schreiben, wird er Ihnen sicherlich dabei helfen, Ihr Wissen zu organisieren. Bitte weisen Sie auf Punkte hin, die nicht erreicht werden können.
Recommended Posts