This is an old revision of the document!
For Bytecode instructions checkcast and instanceof the type must be checked during runtime. Two cases must be considered: checking for an array class or checking for a regular class. Checking for interfaces is discussed below.
Since the dynamic type of an object cannot be known the type checking for any type must work properly. The following table shows all possible casses. ClassA is a regular class, [S is an array of base type (here array of short) and [ClassA is an array of ClassA.
If the object is of type [ClassA and is checked for type [IA (while ClassA implements interface IA), the check must return true. This is ok because under point 6. of the last case the type check will get the type descriptor of ClassA which contains IA. Further details can be found further down in Type Check for Interface Type.
The opposite case must work as well. An object is of type [IA und es wird auf [IA oder auch ein Superinterface von IA geprüft. Im Punkt 6. wird ebenfalls im TD auf den Interfacetyp geprüft. Ist das Objekt vom Typ [IA und wird auf [ClassA geprüft, wird wiederum im Punkt 6. im TD auf den Typ ClassA geprüft. Aus diesem Grund müssen die Descriptoren der Interfaceklassen gleich die wie Typdescriptoren der Standardklassen auch alle Basistypen eingetragen haben. Das ist bei den Interfaces nur Object, alle anderen Einträge müssen leer sein (siehe unten Typprüfung auf normale Klasse).
Hinweis: Sobald ein Typtest auf ein Array von Interfaces gemacht wird, wird für dieses Interface ein Typdescriptor erstellt. Wenn das Objekt zur Laufzeit aber vom Typ Array of Std-Klasse ist, wird der Interface-Typdescriptor gar nicht verwendet. Das ist erst der Fall, wenn der Typ zur Laufzeit auch selber Array of Interfaces ist.
Wenn auf den Typ Object geprüft wird, muss diese Prüfung auch im Falle eines Arrays true liefern.
Wird auf ein Array of Object geprüft, sind zwei Fälle zu unterscheiden
Die obenstehende Beschreibung galt für instanceof. Für checkcast ändert sich folgendes:
Die Instruktionen checkcast und instanceof holen als Operanden eine Objektreferenz vom Stapel. Der zu prüfende Typ wird im Konstantenpool referenziert. Die Typprüfung läuft folgendermassen ab:
Der Typdescriptor einer Klasse ist wie folgt aufgebaut. Basisklasse 0 ist stets Object. Gespeichert wird dabei die Adresse des Typdescriptors von Object. Dann folgen alle weiteren Oberklassen. Die Adresse der eigenen Klasse wird als letzter Eintrag ebenfalls eingefügt. Alle abgeleiteten Typen müssen die gleiche Reihenfolge der Klassen aufweisen, wie die Basisklassen. Das folgende Beispiel soll den Ablauf veranschaulichen.
Für den folgenden Code
ClassA1 o = new ClassA20(); ((ClassA20)o).m1();
berechnet der Compiler den Offset 2 im Typdescriptor von ClassA20, weil die Klasse ClassA20 die 2. Erweiterung von Object ist. Die Objektreferenz o zeigt zur Laufzeit auf ClassA20. Daher wird die Adresse von ClassA20 aus dem Typdescriptor geholt und mit ClassA20 verglichen. Die Typenprüfung ist erfolgreich.
ClassA1 o = new ClassA3(); ((ClassA20)o).m1();
Hier berechnet der Compiler ebenfalls den Offset 2. Die Objektreferenz o zeigt zur Laufzeit auf ClassA3. Mit dem Offset 2 wird ebenfalls die Adresse von ClassA20 geholt und die Typenprüfung ist erfolgreich.
ClassA1 o = new ClassA21(); ((ClassA20)o).m1();
Auch hier berechnet der Compiler den Offset 2. Die Objektreferenz o zeigt jetzt aber auf ClassA21. Mit dem Offset 2 wird die Adresse von ClassA21 geholt und mit ClassA20 verglichen. Die Typenprüfung muss fehlschlagen.
Es muss sichergestellt werden, dass alle Typdecriptoren die gleiche Anzahl von Einträgen aufweisen. Das bedeutet, dass die Descriptoren der Basisklassen mit der notwendigen Anzahl von leeren Einträgen gefüllt werden müssen. Damit wird vermieden, dass ungültige Adressen gelesen werden können. Als Beispiel dazu:
ClassA1 o = new ClassA21(); ((ClassA3)o).m1();
Hier wird der Offset 3 berechnet. Im Typdescriptor von ClassA21 darf beim Offset 3 kein zufälliger anderer Eintrag vorhanden sein.
Warum müssen alle Typdescriptoren die gleiche (maximale) Anzahl von Einträgen aufweisen? Nehmen wir den Code
Object o; ((ClassX)o).m1();
Weil o vom Typ statischen Typ Object ist, der dynamische Typ aber beliebig sein kann, kann hier auf einen beliebigen Typ geprüft werden. ClassX könnte z.B. den Offset 10 haben. Also müssen alle Typdescriptoren mindestens 10 (wenn auch leere) Einträge haben.
Interfaceklassen dürfen nicht in der Tabelle mit den Standardklassen aufgeführt werden. Der folgende Fall zeigt warum:
Je nach Vererbungshierarchie belegt das Interface einen anderen Platz in der Tabelle und der Compiler kann keinen fixen Offset berechnen mit dem in der Tabelle geprüft werden könnte.
Aus diesem Grund gibt es eine Tabelle im Typdescriptor mit allen Interfaces, die diese Klasse implementiert und auf die überhaupt je ein Typtest gemacht wird.
Die betroffenen Interfaces werden alle nummeriert (chkId) und in der Tabelle beginnend mit der grössten Nummer eingetragen. Die Typprüfung läuft nun so ab, dass der Compiler die chkId der zu testenden Klasse in der Tabelle des Typdescritors des aktuellen Typs sucht. Wird sie gefunden, liefert der Test true andernfalls false. Der letzte Eintrag in der Tabelle muss 0 sein.
Auch hier muss wieder wie oben ein Unterschied zwischen checkcast und instanceof gemacht werden.
Der folgende Code
Object[][][] a1 = new Object[2][3][4]; A[] x = new AA[5]; a1[0][0] = x; a1[0][1][2] = x; short[][] y = new short[2][3]; a1[1][0] = y; a1[1][1][0] = y;
führt auf die folgende Abbildung
Dieses Beispiel befindet sich auch in den Testfällen (ch.ntb.inf.deep.comp.targettest.arrays.ArrayInstanceTest.testInstance4).