Unterschiede beim automatischen Entpacken zwischen Java 6 und Java 7


107

Ich habe einen Unterschied im Verhalten beim automatischen Entpacken zwischen Java SE 6 und Java SE 7 festgestellt. Ich frage mich, warum dies so ist, da ich keine Dokumentation zu Änderungen in diesem Verhalten zwischen diesen beiden Versionen finden kann.

Hier ist ein einfaches Beispiel:

Object[] objs = new Object[2];
objs[0] = new Integer(5);
int myInt = (int)objs[0];

Dies lässt sich gut mit Javac aus Java SE 7 kompilieren. Wenn ich dem Compiler jedoch das Argument "-source 1.6" gebe, wird in der letzten Zeile ein Fehler angezeigt:

inconvertible types
found   : java.lang.Object
required: int

Ich habe versucht, Java SE 6 herunterzuladen, um es mit dem nativen Compiler der Version 6 zu kompilieren (ohne die Option -source). Es stimmt zu und gibt den gleichen Fehler wie oben.

Also, was gibt es? Aus einigen weiteren Experimenten geht hervor, dass das Unboxing in Java 6 nur Werte entpacken kann, die eindeutig (zur Kompilierungszeit) vom Typ Boxed sind. Dies funktioniert beispielsweise in beiden Versionen:

Integer[] objs = new Integer[2];
objs[0] = new Integer(5);
int myInt = (int)objs[0];

Es scheint also, dass zwischen Java 6 und 7 die Unboxing-Funktion so erweitert wurde, dass Objekttypen auf einen Schlag umgewandelt und entpackt werden können, ohne (zur Kompilierungszeit) zu wissen, dass der Wert vom richtigen Box-Typ ist. Beim Lesen der Java-Sprachspezifikation oder der Blog-Beiträge, die zum Zeitpunkt der Veröffentlichung von Java 7 geschrieben wurden, kann ich jedoch keine Änderung dieser Sache feststellen. Daher frage ich mich, wie die Änderung lautet und wie diese "Funktion" heißt ?

Nur eine Kuriosität: Aufgrund der Änderung ist es möglich, "falsche" Unboxings auszulösen:

Object[] objs = new Float[2];
objs[0] = new Float(5);
int myInt = (int)objs[0];

Dies lässt sich gut kompilieren, gibt jedoch zur Laufzeit eine ClassCastException aus.

Irgendein Hinweis dazu?


17
Interessant. Eine neue Zutat für das Autoboxing-Chaos. Ich denke, Ihr Beispiel könnte einfacher und klarer mit einem einzelnen Objekt anstelle eines Arrays sein. Integer obj = new Integer(2); int x = (int)obj;: funktioniert auf Java 7, gibt Fehler auf Java 6.
Leonbloy

1
Welches JDK verwenden Sie? Es könnte auch mit verschiedenen Anbietern zu tun haben ...
Barfuin

1
@leonbloy: Guter Punkt zur Vereinfachung, ich habe es etwas vereinfacht (von meinem ursprünglichen Code), aber irgendwie zu früh aufgehört!
Morty

@Thomas: Es war das neueste JDK (für jede Version) von Oracle, das ich verwendet habe.
Morty

2
Ein weiterer Grund, Autoboxing niemals zu verwenden.
Gyorgyabraham

Antworten:


92

Es sieht so aus, als ob die Sprache in Abschnitt 5.5 Casting-Konvertierung von Java 7 JLS im Vergleich zu demselben Abschnitt in Java 5/6 JLS aktualisiert wurde , wahrscheinlich um die zulässigen Konvertierungen zu verdeutlichen.

Java 7 JLS sagt

Ein Ausdruck eines Referenztyps kann durch Unboxing-Konvertierung fehlerfrei in einen primitiven Typ umgewandelt werden.

Java 5/6:

Ein Wert eines Referenztyps kann durch Unboxing-Konvertierung in einen primitiven Typ umgewandelt werden (§5.1.8).

Das Java 7 JLS enthält auch eine Tabelle (Tabelle 5.1) der zulässigen Konvertierungen (diese Tabelle ist in Java 5/6 JLS nicht enthalten) von Referenztypen zu Grundelementen. Dadurch werden Umwandlungen von Objekt zu Grundelementen explizit als einschränkende Referenzkonvertierung mit Unboxing aufgeführt.

Der Grund wird in dieser E-Mail erklärt :

Fazit: Wenn die Spezifikation. erlaubt (Objekt) (int) es muss auch erlauben (int) (Objekt).


35

Du hast recht; einfacher ausgedrückt:

Object o = new Integer(1234);
int x = (int) o;

Dies funktioniert in Java 7, führt jedoch zu einem Kompilierungsfehler in Java 6 und darunter. Seltsamerweise ist diese Funktion nicht prominent dokumentiert. Zum Beispiel wird es hier nicht erwähnt . Es ist fraglich, ob es sich um eine neue Funktion oder eine Fehlerbehebung (oder einen neuen Fehler?) Handelt. Weitere Informationen und Diskussionen finden Sie hier . Der Konsens scheint auf eine Unklarheit in der ursprünglichen Spezifikation hinzuweisen , die zu einer leicht inkorrekten / inkonsistenten Implementierung auf Java 5/6 führte, die in 7 behoben wurde, da dies für die Implementierung von JSR 292 (Dynamically Typed Languages) von entscheidender Bedeutung war.

Java Autoboxing hat jetzt einige weitere Fallen und Überraschungen. Beispielsweise

Object obj = new Integer(1234);
long x = (long)obj;

wird kompiliert, schlägt aber ClassCastExceptionzur Laufzeit fehl (mit ). Dies wird stattdessen funktionieren:

long x = (long)(int)obj;


2
Danke für die Antwort. Eines verstehe ich jedoch nicht. Dies ist eine Klarstellung des JLS und der zugehörigen Implementierungen (vgl. Die Mail-Diskussion), aber warum sollte dies getan werden, um andere typisierte Sprachen in der JVM aufzunehmen? Schließlich handelt es sich um eine Änderung der Sprache und nicht der VM: Das Casting-Verhalten der VM funktioniert wie immer. Der Compiler implementiert diese Funktion unter Verwendung des vorhandenen Mechanismus zum Casting in Integer und zum Aufrufen von .intValue (). Wie kann diese Änderung der eigentlichen Java-Sprache dazu beitragen, andere Sprachen auf der VM auszuführen? Ich bin damit einverstanden, dass Ihr Link dies vorschlägt und sich nur wundert.
Morty
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.