Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden. Du solltest ein Upgrade durchführen oder ein alternativer Browser verwenden.
Ich mein, links steht doch der genaue Typ. Wieso kann er dann nicht davon ableiten, dass "Pocket.newInstance()" auch vom Typ "Pocket<String>" sein muss?
Das geht mit einem Trick... ist aber mit Vorsicht zu geniessen.
Java:
class Pocket<T> {
public static <T> Pocket<T> getInstance() {
// Wie kann ein Singleton eigentlich generisch sein?
// Wenn dir hier also etwas sinnvolles einfaellt...
// immer rein damit ;)
retutn (Pocket<T>) instance;
}
}
So... Singleton steht dort, weil du die dafür vorgesehene Methode zum besorgen der Instanz verwendet hast. Für so etwas ist Singleton aber keinesfalls sinnvoll, weil die Methode parametrisiert (evtl. per [c]getInstance(Class<T> clazz)[/c] o.ä.) werden müsste. Dann wär's aber 'ne Factory.
Du musst entweder mit Java 7 compilieren - oder Pocket<String> nochmal schreiben. Type inference bezieht sich nur auf die Überprüfung, ob alle Typen miteinander beim Compilieren vereinbar sind
Die Methode newInstance muss selbst generisch deklariert werden , wie das geht, hat Spacerat schon skizziert. Aber ganz so, wie es in seinem Code steht, geht es nicht. Lies Dir den nachfolgenden Code durch. Das müsste es klar machen.
Java:
// Das <T> VOR dem return type (Pocket) macht die Methode
// generisch.
//-------------v----------------
public static <T> Pocket<T> newInstance() {
return new Pocket<T>();
}
Außerdem gibt es Probleme mit Type Inference beim ternären Operator. Schreibe die Zuweisung in ein if-else-Konstrukt um:
Wieso soll das im "if" ein Problem sein. Der Compiler weiß doch von der ersten Zeile das es sich hier um ein "Pocket<String>" handelt, also müsste ja "newInstance()" ein Ergebnis vom typ "Pocket<String>" liefern?
UPSI... hatte mich mit "getInstance()" wohl verlesen.
@nillehammer: Deswegen funktioniert mein Code ja auch nicht, weil die Pocket-Instance ja noch gar nicht existiert... Dein Code funktioniert aber. Wenigstens einer der aufpasst.
Ich mein, links steht doch der genaue Typ. Wieso kann er dann nicht davon ableiten, dass "Pocket.newInstance()" auch vom Typ "Pocket<String>" sein muss?
Ich habe die Erfahrung gemach, dass sich der ternäre Operator bei der Typinferenz etwas zickig hat. In vielen Fällen könnte problemlos ein gemeinsamer Typ abgeleitet werden, aber wenn das nicht genau stimmt, meckert der Compiler. Und wenn es geht, ist es auch nicht immer koscher:
Java:
Number x = false ? Double.valueOf(3.3) : Integer.valueOf(7);
System.out.println(x); // 7.0
System.out.println(x.getClass()); //class java.lang.Double
Und für die Interessierten weshalb sich der ternäre Operator so verhält wie er sich verhält gibt es eine Erläuterung in dem schon etwas älteren Ticket Bug ID: 6721089 ternary conditional operator does not handle generics.
Aber Number ist ja kein echter Zahlenwert, sondern eine reine Oberklasse für Zahlen. List ist ja auch nur ein Interface für Listen. Daher würdest du ja auch immer die implementierende Klasse bekommen und nicht List und bei Zahlenwerten hast du halt immer noch seltsame Zusatzeffekte vom Autoboxing dazu.
Ich schrub: [c]Number x = false ? Double.valueOf(3.3) : Integer.valueOf(7);[/c].
Sind wir uns einig, dass [c]Double.valueOf(3.3)[/c] ein [c]Double[/c] und [c]Integer.valueOf(7)[/c] ein [c]Integer[/c] ist? Wieso sollte dann irgend welches Boxing stattfinden, wenn das "Ziel" [c]Number[/c] ist?
Ich schrub: [c]Number x = false ? Double.valueOf(3.3) : Integer.valueOf(7);[/c].
Sind wir uns einig, dass [c]Double.valueOf(3.3)[/c] ein [c]Double[/c] und [c]Integer.valueOf(7)[/c] ein [c]Integer[/c] ist? Wieso sollte dann irgend welches Boxing stattfinden, wenn das "Ziel" [c]Number[/c] ist?
Äh... ja. An dieser Stelle wird's also wieder ein WTF. Wär mal interessant zu erfahren, warum die JVM da so etwas macht, selbst wenn Noctarius Erklärung zunächst einleuchtend erschien. Ob man die betreffenden Beiträge evtl. abkoppeln könnte? Ist's evtl ein Bug?
Da es sich also bei dem Beispiel um zwei Typen handelt die in primitive numerische Werte überführt werden können (was durch Unboxing Conversion geschieht) wird eine "Binary Numeric Promotion" (die zwei Werte als Argument bekommt) vorgenommen, die besagt, dass wenn einer der Beteiligten ein double ist der andere auch zu einem double wird. Laut Definition ist das Ergebnis des Conditional dann ein double. In unserem Fall wird dieser double wieder heimlich in einen Double überführt und schließlich implizit auf Number gecastet.
Das wäre das einleuchtende bei Noctarius Erklärung gewesen. Aber ich kann mich da Landei's Frage nur anschliessen. Was genau muss da geboxed werden und vor allem warum? Beide Argumente hinter dem Fragezeichen sind bereits vom Typ Number. Und solange Autoboxing die Erklärung bleibt, weil's die JVM fälschlicherweise als [c]Number n = (false)? 3.3 : 7;[/c] interpretiert, ist's ein Bug. Deine Links befassen sich ja nirgends mit diesem Fall, sonder halt nur mit Primitivtypen, wo das Verhalten prinzipiell klar ist.
Das steht in dem dritten Link. Zitat: "If any operand is of a reference type, it is subjected to unboxing conversion (§5.1.8)". D.h. der Conditional bekommt hinter dem Vorhang einen int und double. Und der int wird zu double promoted. Das steht da aber Alles.
Da hab' ich wohl nicht weit genug weiter gelesen, halt nur den Teil, wo's um Autoboxing von Primitiven ging.
Aber wie auch immer. Die Tatsache, dass es den User überrascht, bleibt unverändert. Normalerweise soll ja jede Short-Condition so arbeiten, wie ihr ausgewalztes Pendant. Das ist hier aber nicht der Fall. Normalerweise wird die Short-Condition auch als ausgewalzter if-else-clause übersetzt.
Dabei muss doch eigentlich schon auffallen (okay, in meinem Beispiel eher weniger. XD), dass nichts geboxed werden muss. Aber worauf ich hinaus will: Landeis Beispiel und das ausgewalzte Pendant dazu, liefern definitiv verschiedene Resultate und das darf meiner Meinung nach nicht sein.
Java:
public static Number shortCondition(int x, int y) {
Number rc = null;
rc = (x < y)? Double.valueOf(3.3) : Integer.valueOf(7);
return rc;
}
public static Number ifElseClause(int x, int y) {
Number rc = null;
if(x < y) {
rc = Double.valueOf(3.3);
} else {
rc = Integer.valueOf(7);
}
return rc;
}
Da dürfte meiner Meinung nach erst geboxed werden, wenn der "normale" ifElseClause nicht übersetzt werden kann.
Apropos auswalzen... Nach was müsste man eigentlich wo suchen, wenn man wissen will, ob etwas derartiges schon gemeldet wurde?
Das ist eine Annahme von dir die falsch ist, da in der JLS nichts darüber gesagt wird, dass das if-else-Statement ein Verhalten identisch zum Conditional-Operator besitzen muss. Status quo ist das beide unterschiedliche Dinge sind die ein ähnliches und in manchen Konstruktionen auch das selbe Verhalten aufweisen. Das die beiden Dinger nicht gleich sind ist auch leicht einzusehen wenn man sich die Definition des if-then-else Statements und die des Conditional Operators ansieht.
Das das in keiner Sprachspezifikation auftaucht, liegt wohl daran, dass die Funktion des ternären Operators (auch oft als Short-Condition bezeichnet) in jeder anderen Sprache (ausser PHP vllt.) eigentlich klar ist. Es ist ganz allgemein If-Else in Kurzform, warum also sollte es jemals irgendwo Unterschiede zwischen beiden geben. Das ist auch keinesfalls nur eine Annahme von mir, sondern es wird im Allgemeinen so erwartet.
Was aber eine Annahme von mir ist; Ich denke, das Landeis Beispiel vor der Einführung des Auto In/Out Boxing noch korrekt funktioniert hat, obwohl - das ist eigentlich auch offensichtlich. Man könnte den Java-Entwicklern also reinen Gewissens unterstellen, dass sie dort die Wrapperklassen schlicht aus dem selben Grund autoboxen, aus welchem sie eine Short-Condition statt If-Else verwenden würden, nur ist hier diese "Faulheit" ebenso wie das Autoboxing eher unangebracht.
In welchen anderen Sprach-Spezifikation (ausser Java und PHP) sonst, wären diese beiden Definitionen denn auch nicht gleich? Wieso wird beides ausser bei Wrapperklassen immer gleich übersetzt?
Der Grund ist klar, den will ich auch nicht diskutieren, aber sowas zu finden (wenn man z.B. beide Werte per Parameter in die Methode bekommt, die Methode nicht selber geschrieben hat und sich eigentlich nur wundert wieso das so unterschiedlich ist, ätzend ;-)
Nein das hätte es nicht, da wäre das überhaupt nicht möglich gewesen sondern es gäbe einen Compiler-Error das ein "double" kein Number ist. Du hättest "x" als double definieren müssen um es fehlerfrei übersetzen zu können.
Ich muss auch einen kleinen Teil meiner Aussagen zum Unboxing revidieren, dem Conditional werden in dem Beispiel ja sogar schon die primitiven Typen übergeben, also fällt das weg. Es findet wohl nur noch eine Binary Numeric Promotion des int auf einen double statt und dann das versteckte Boxing von double auf Double.
Also im Endeffekt findet das was man vor Java 5 offensichtlich gesehen hat und auch aktiv beeinflussen musste nun hinter dem Vorhang statt, was dann zu überraschten Gesichtern führt.
Edit:
Vor 5 hätte der Code so ähnlich aussehen müssen:
Java:
boolean value = false;
Number x = new Double(value ? new Double(3.3).doubleValue() : new Integer(7).intValue());