Compiler-Fehler Oracle javac 7 nicht kompatibel zu Sun javac 6

Ebenius

Top Contributor
Huhu,

ich bin grad ganz verwundert. Dieses Stück Java:
Java:
public class CompilerVarArgsTest {

  private static void foo(Object... args) {}

  private static void foo(int i2, Object... args) {}

  public static void main(String[] args) {
    foo(1, Integer.valueOf(0), "second arg");
  }
}
… kann ich mit dem javac aus dem Sun-JDK 6 (JLS 3) kompilieren. Mit dem javac aus dem Oracle-JDK 7 (ebenfalls gegen JLS 3!) funktioniert das aber nicht. :noe: Ist das ein bekannter Compiler-Bug oder hab ich nen Denkfehler?

Code:
me@myhost:~/devel/workspace-rsm/Playground/src$ /usr/lib/jvm/java-6-sun/bin/javac -source 1.5 -target 1.5 CompilerVarArgsTest.java 
me@myhost:~/devel/workspace-rsm/Playground/src$ /usr/lib/jvm/java-7-oracle/bin/javac -source 1.5 -target 1.5 CompilerVarArgsTest.java 
warning: [options] bootstrap class path not set in conjunction with -source 1.5
CompilerVarArgsTest.java:8: error: reference to foo is ambiguous, both method foo(Object...) in CompilerVarArgsTest and method foo(int,Object...) in CompilerVarArgsTest match
    foo(1, Integer.valueOf(0), "second arg");
    ^
1 error
1 warning
😡

[EDIT]BTW: Mein JDT ist natürlich der selben Meinung wie der Sun-6-javac. :smoke:[/EDIT]
Grüße, Ebenius
 
Zuletzt bearbeitet:
oO welche Methode verwendet denn das JDK6?

Finde ich aber ok, dass das angeprangert wird. Könnte sonst zu unschönen/unerwarteten Seiteneffekten führen.
 
Es verwendet die konkretere, also die zweite [c]foo()[/c]. Und das deckt sich auch mit der JLS 3 (soweit ich das in Erinnerung habe). Eine Warnung hätte ich ja auch noch verstanden. Aber einfach einen Fehler schmeißen und abbrechen, obwohl ich source-compliance 1.5 mit angebe, das ist ein No-Go!

Und nö, das Anzuprangern ist gar nicht nett. Das eigentliche Beispiel sieht so aus:
Java:
  public static int showConfirmDialog(
        Component parentComponent,
        String msg,
        int optionType,
        Object... args) {
    // ...
  }

  public static int showConfirmDialog(
        Component parentComponent,
        String msg,
        int optionType,
        int messageType,
        Object... args) {
    // ...
  }
Und dabei ist's völlig logisch, dass der Aufruf [c]showConfirmDialog(frame, "Agree, that something is wrong with {0}?", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, "Oracle")[/c] auf die zweite Methode verweist.

Grundsätzlich dürfen verschiedene Compiler für die selbe Sprache einfach nicht unterschiedlicher Meinung sein, was erlaubt ist und was nicht. 🙁

Ebenius
 
Dass der Code kompiliert wurde, scheint ein Bug gewesen zu sein, der mit Java 7 gefixt wurde: 7115209 : Compiler regression with ambiguous varargs methods

JLS-Referenz: 15.12.2.5. Choosing the Most Specific Method
Siehe "Changes in Most Specific Varargs Method Selection": Incompatibilities between JDK 7 and JDK 6
RFE, der dazu geführt hat: Bug ID: 6199075 Unambiguous varargs method calls flagged as ambiguous

Kurzbeschreibung:
Code:
int
ist kein Subtyp von
Code:
Object
AND
Code:
Object
ist kein Subtyp von
Code:
int
=> ambiguous
"Workaround": In der Methodensignatur
Code:
Integer
statt
Code:
int
, weil
Code:
Integer
ein Subtyp von
Code:
Object
ist.

Und warum das mit source-compliance nicht funktioniert: Keine Ahnung.
 
Zuletzt bearbeitet:
Jupp, hab ich auch inzwischen gefunden. 🙂 Danke.

Dennoch find ich's blöd, dass der Compiler mit -source 1.5 nicht nach dem alten Prinzip verhält…

Ebenius
 
Ich verstehe es nicht ganz, würde es aber gerne nachvollziehen. Ich hänge bei dieser Aussage von Bug ID: 7115209 Compiler regression with ambiguous varargs methods :

- Is (boolean,Object[]) more specific than (Object[]) ? No, because the boolean formal parameter cannot accept an Object.

Macht für mich irgendwie wenig Sinn. Wenn ich einen Aufruf mit zB (true, "Test", null) mache, wieso hat er ein Problem das mit der ersten Methode zu matchen?
 
Ich verstehe es nicht ganz, würde es aber gerne nachvollziehen. Ich hänge bei dieser Aussage von Bug ID: 7115209 Compiler regression with ambiguous varargs methods :



Macht für mich irgendwie wenig Sinn. Wenn ich einen Aufruf mit zB (true, "Test", null) mache, wieso hat er ein Problem das mit der ersten Methode zu matchen?
Ich vermute mal Autoboxing kann schnell aus einem boolean ein Boolean machen und man merkt das nicht direkt, etc. pp.
 
Wenn ich einen Aufruf mit zB (true, "Test", null) mache, wieso hat er ein Problem das mit der ersten Methode zu matchen?
Wenn eine Methode "m(boolean a, Object ... args)" und eine Methode "m(Object ... args)" existiert, wüsste er nicht, welche Methode er nehmen soll, weil ein boolean immer noch in ein Boolean geboxed werden kann (Autoboxing). Der Compiler erwartet deswegen einen expliziten Cast des booleans zu boolean (also dem was es ist), Boolean oder Object. In den letzen beiden Fällen würde dann natürlich "m(Object ... args)" aufgerufen.
Ist schon länger her, da hab' ich eine dieses Problem betreffende Frage mal hier im Forum gestellt und mir wollte keiner glauben, was da abging. Ich hatte mich damals gewundert, wieso mit "m(x, y, z)" (alles doubles) bei den vorhandenen Methoden "m(Object ... args)" und "m(double x, double y, double z)" erstere statt letztere aufgerufen wird. Naja, seit Java 7 weis ich's 😉.
[EDIT]Und wofür ich 'ne virtel Stunde brauch' schafft's maki in wenigen Minuten...[/EDIT]
 
Zuletzt bearbeitet von einem Moderator:
Ne, der explizite Cast (zu boolean, bzw. in meinem Beispiel int) hilft Dir hier gar nicht. 🙂

[EDIT]Der explizite Cast zu Object würde helfen, die (falsche) Methode zu rufen.[/EDIT]

Ebenius
Oi... das ist neu. Bin mir sicher, dass es bei mir damals so ging. Und wie will man die "richtige" Methode nun aufrufen?
 
Wenn ich das richtig verstanden habe, geht es nur mit meinem bereits genannten Workaround (Boxing types in die Signatur nehmen), wenn man beim Aufruf von den Varargs Gebrauch machen will. Ansonsten übergibt man eben explizit das Array.

Auch kurios in dem Zusammenhang:
Java:
public class AmbiguousVarargsTest {
	static void a(int a, long b) {}
	static void a(long a, int b) {}
	static void a(int... c) {}
	static void a() {a(0, 0);}
}
Hier wird die Varargs-Methode aufgerufen. Kommentiert man eine der beiden oberen Methoden aus, wird stattdessen die jeweils andere aufgerufen.
Und kommentiert man die Varargs-Methode aus, gibt es den bereits bekannten Kompilierfehler.
… da fällt mir so auf, dass das auch gut in den Quiz-Thread gepasst hätte. 🙂
 
Zuletzt bearbeitet:
Also bei mir ruft er immer die spezifischste Methode auf? Oo
ich hab hier überhaupt kein Problem... mit JDK 7 und JRE 7

Ist bei mir nun irgendwas kaputt? 😀
 

Zurück
Oben