Schauen wir uns den einfachen Java-Code im folgenden Snippet an:
public class Main {
private int temp() {
return true ? null : 0;
// No compiler error - the compiler allows a return value of null
// in a method signature that returns an int.
}
private int same() {
if (true) {
return null;
// The same is not possible with if,
// and causes a compile-time error - incompatible types.
} else {
return 0;
}
}
public static void main(String[] args) {
Main m = new Main();
System.out.println(m.temp());
System.out.println(m.same());
}
}
In diesem einfachsten Java-Code gibt die temp()
Methode keinen Compilerfehler aus, obwohl der Rückgabetyp der Funktion lautet int
, und wir versuchen, den Wert null
(über die Anweisung return true ? null : 0;
) zurückzugeben. Beim Kompilieren verursacht dies offensichtlich die Laufzeitausnahme NullPointerException
.
Allerdings scheint es , dass die gleiche Sache falsch ist , wenn wir den ternären Operator mit einer darstellen if
Aussage (wie im same()
Verfahren), das tut einen Fehler bei der Kompilierung Ausgabe! Warum?
int foo = (true ? null : 0)
undnew Integer(null)
beide kompilieren gut, wobei die zweite die explizite Form des Autoboxing ist.null
zuInteger
... Das würde so aussehen, als würde ich "raten" oder "Dinge zum Laufen bringen " ...Integer foo() { return "1"; }
wird nicht kompiliert.)Antworten:
Der Compiler interpretiert
null
als Nullreferenz auf aInteger
, wendet die Autoboxing- / Unboxing-Regeln für den bedingten Operator an (wie in der Java-Sprachspezifikation, 15.25 beschrieben ) und fährt glücklich fort. Dadurch wirdNullPointerException
zur Laufzeit eine generiert , die Sie durch Ausprobieren bestätigen können.quelle
capture conversion
undlub(T1,T2)
) ?? Ist es wirklich möglich, Boxen auf einen Nullwert anzuwenden? Wäre das nicht wie "Raten"?lub(T1,T2)
ist der spezifischste Referenztyp, der in der Typhierarchie von T1 und T2 gemeinsam ist. (Beide teilen sich mindestens Object, daher gibt es immer einen bestimmten Referenztyp.)null
nicht boxed in eine Integer, wird interpretiert als Hinweis auf einen Integer (einen NULL - Verweis, aber das ist kein Problem). Aus der Null wird kein Integer-Objekt erstellt, daher gibt es keinen Grund für eine NumberFormatException.null
(was kein primitiver numerischer Typ ist), lautet die anwendbare Klausel "Wenn p ein Wert eines anderen Typs ist, entspricht die Boxkonvertierung einer Identitätskonvertierung ". Also Boxing-Konvertierungnull
inInteger
Ausbeutennull
, ohne einenInteger
Konstruktor aufzurufen .Ich denke, der Java-Compiler interpretiert
true ? null : 0
alsInteger
Ausdruck, der implizit konvertiert werden kannint
und möglicherweise gibtNullPointerException
.Für den zweiten Fall ist der Ausdruck
null
vom speziellen Nulltyp ( siehe) , sodass der Codereturn null
den Typ nicht übereinstimmt.quelle
true ? null : 0
alsInteger
? Durch Autoboxing0
zuerst?Eigentlich ist alles in der Java-Sprachspezifikation erklärt .
Daher
(true ? null : 0)
erhält die "Null" in Ihrem einen int-Typ und wird dann automatisch in eine Ganzzahl geschrieben.Versuchen Sie etwas Ähnliches, um dies zu überprüfen,
(true ? null : null)
und Sie erhalten den Compilerfehler.quelle
int
Wert von der Funktion zurückzugeben, der eine NPE verursacht.null
zuInteger
mitnew Integer(null);
„Let T1 sein , die Art , dass die Ergebnisse aus der Anwendung Boxen Umstellung auf S1 ...“ Sie bekommen würden ,NumberFormatException
und dies ist nicht der Fall ...Im Fall der
if
Anweisung wird dienull
Referenz nicht alsInteger
Referenz behandelt, da sie nicht an einem Ausdruck teilnimmt , der die Interpretation als solche erzwingt. Daher kann der Fehler leicht zur Compile-Zeit gefangen werden , weil es klarer ist ein Typ Fehler.Was den bedingten Operator betrifft, so
? :
beantwortet die Java-Sprachspezifikation §15.25 „Bedingter Operator “ dies in den Regeln für die Anwendung der Typkonvertierung:quelle
0
ein Autobox aktiviert ist, führtInteger
der Compiler den letzten Fall der "ternären Operatorregeln" aus, wie in der Java-Sprachspezifikation beschrieben. Wenn das stimmt, fällt es mir schwer zu glauben, dass es dann zu Fall 3 derselben Regeln springen würde, die eine Null und einen Referenztyp haben, bei denen der Rückgabewert des ternären Operators der Referenztyp (Integer) ist. .Integer
? Genau das passiert; Die NPE wird generiert, indem versucht wird, den Ausdruckswert zu entpacken, um einenint
von der Funktion zurückzugeben. Ändern Sie die Funktion, um eine zurückzugeben,Integer
und sie wirdnull
ohne Probleme zurückgegeben.null
in diese Kategorie fällt . Außerdem würden wir dann in den Schritt "Andernfalls wird die binäre numerische Heraufstufung (§5.6.2) angewendet ... Beachten Sie, dass die binäre numerische Heraufstufung eine Unboxing-Konvertierung (§5.1.8) ..." ausführt, um den Rückgabetyp zu bestimmen. Die Unboxing-Konvertierung würde jedoch eine NPE generieren. Dies geschieht nur zur Laufzeit und nicht beim Versuch, den ternären Operatortyp zu bestimmen. Ich bin immer noch verwirrt.null
wird so behandelt, als hätte es Typint
, ist aber eigentlich gleichbedeutend mitthrow new NullPointerException()
, das ist alles.Das erste, was zu beachten ist, ist, dass ternäre Java-Operatoren einen "Typ" haben und dass dies vom Compiler bestimmt und berücksichtigt wird, unabhängig davon, welche tatsächlichen / realen Typen der zweite oder dritte Parameter sind. Abhängig von mehreren Faktoren wird der ternäre Operatortyp auf unterschiedliche Weise bestimmt, wie in der Java-Sprachspezifikation 15.26 dargestellt
In der obigen Frage sollten wir den letzten Fall betrachten:
Dies ist bei weitem der komplexeste Fall, wenn Sie sich die Anwendung der Capture-Konvertierung (§5.1.10) und vor allem lub (T1, T2) ansehen .
Im Klartext und nach einer extremen Vereinfachung können wir den Prozess als Berechnung der "Least Common Superclass" (ja, denken Sie an das LCM) des zweiten und dritten Parameters beschreiben. Dies gibt uns den ternären Operator "Typ". Was ich gerade gesagt habe, ist eine extreme Vereinfachung (betrachten Sie Klassen, die mehrere gemeinsame Schnittstellen implementieren).
Wenn Sie beispielsweise Folgendes versuchen:
Sie werden feststellen, dass der resultierende Typ des bedingten Ausdrucks
java.util.Date
die "Least Common Superclass" für dasTimestamp
/Time
pair ist.Da
null
für alles eine Autobox erstellt werden kann, ist die "Least Common Superclass" dieInteger
Klasse und dies ist der Rückgabetyp des obigen bedingten Ausdrucks (ternärer Operator). Der Rückgabewert ist dann ein Nullzeiger vom Typ,Integer
und dieser wird vom ternären Operator zurückgegeben.Zur Laufzeit, wenn die Java Virtual Machine entpackt, wird das
Integer
aNullPointerException
ausgelöst. Dies geschieht, weil die JVM versucht, die Funktion aufzurufennull.intValue()
,null
was das Ergebnis einer Autobox ist.Meiner Meinung nach (und da meine Meinung nicht in der Java-Sprachspezifikation enthalten ist, werden viele Leute es sowieso falsch finden) leistet der Compiler schlechte Arbeit bei der Bewertung des Ausdrucks in Ihrer Frage. Vorausgesetzt, Sie haben geschrieben, sollte
true ? param1 : param2
der Compiler sofort bestimmen, dass der erste Parameter -null
- zurückgegeben wird, und er sollte einen Compilerfehler erzeugen. Dies ist etwas ähnlich wie beim Schreibenwhile(true){} etc...
und der Compiler beschwert sich über den Code unter der Schleife und kennzeichnet ihn mitUnreachable Statements
.Dein zweiter Fall ist ziemlich einfach und diese Antwort ist schon zu lang ...;)
KORREKTUR:
Nach einer weiteren Analyse glaube ich, dass ich zu Unrecht gesagt habe, dass ein
null
Wert für alles eingerahmt / autoboxed werden kann. Wenn wir über die Klasse Integer sprechen, besteht explizites Boxen darin, dennew Integer(...)
Konstruktor oder vielleicht das aufzurufenInteger.valueOf(int i);
(ich habe diese Version irgendwo gefunden). Ersteres würde ein werfenNumberFormatException
(und das passiert nicht), während das zweite einfach keinen Sinn ergeben würde, da einint
nicht sein kannnull
...quelle
null
Originalcode des OP ist nicht verpackt. So funktioniert es: Der Compiler geht davon aus, dassnull
es sich um eine Referenz auf eine Ganzzahl handelt. Unter Verwendung der Regeln für ternäre Ausdruckstypen wird entschieden, dass der gesamte Ausdruck ein ganzzahliger Ausdruck ist. Es generiert dann Code für die Autobox1
(falls die Bedingung ausgewertet wirdfalse
). Während der Ausführung wird die Bedingung als ausgewertet,true
sodass der Ausdruck als ausgewertet wirdnull
. Wenn Sie versuchen, einint
von der Funktion zurückzugeben,null
wird das nicht ausgepackt. Das wirft dann eine NPE. (Der Compiler könnte das meiste davon weg optimieren.)Tatsächlich kann der Ausdruck im ersten Fall ausgewertet werden, da der Compiler weiß, dass er als ausgewertet werden muss.
Integer
Im zweiten Fall kann der Typ des Rückgabewerts (null
) jedoch nicht bestimmt werden, sodass er nicht kompiliert werden kann. Wenn Sie esInteger
umwandeln, wird der Code kompiliert.quelle
quelle
Wie wäre es damit:
Die Ausgabe ist wahr, wahr.
Die Eclipse-Farbe codiert die 1 im bedingten Ausdruck als Autobox.
Ich vermute, der Compiler sieht den Rückgabetyp des Ausdrucks als Objekt.
quelle