2015-05-28 5 views
11

Wir sind gerade dabei, eine Anwendung von Java 7 nach Java 8 zu migrieren. Nachdem ich einige Kompilierungsprobleme behoben hatte, stieß ich auf ein Problem, das der folgenden Frage ähnelte: ClassCast Error: Java 7 vs Java 8.Wie erkennen Sie mehrdeutige Methodenaufrufe, die eine ClassCastException in Java 8 verursachen würden?

, hier Zusammengefasst ist ein Beispielcode, der das Problem zeigt:

public class Test { 
    public static void main(String[] args) { 
     System.out.println(String.valueOf(getVal("xxx"))); // 7: prints the result, 8: Exception 
    } 

    @SuppressWarnings("unchecked") 
    public static <T> T getVal(String param) { 
     // do some computation based on param... 
     return (T) result; // actual return type only depends on param so the caller knows what to expect 
    } 
} 

Die Idee war, dass wir stoßen würde, dass der Anrufer den erwarteten Typ kennt, und dies würde ihm eine explizite Umwandlung (Ich vermeide sag nicht, das war eine gute Idee ...). In vielen Fällen erwartet der Aufrufer nur eine Object, also gab es überhaupt keine implizite Besetzung.

Wie in der obigen Frage angegeben, funktionierte das String.valueOf Beispiel in Java 7 gut, da keine Typrückschlüsse vorlagen, daher wurde Object angenommen. In Java 8 wählt der Compiler nun den spezifischsten Typ (hier char[]), der zur Laufzeit einen ClastCastException verursacht. Das Problem ist, dass wir etwa 350 Aufrufe dieser getVal Methode haben. Gibt es eine Möglichkeit, überladene Methodenaufrufe zu erkennen, die sich zwischen Java 7 und Java 8 unterscheiden würden? I.E. erkennen, wenn der Java 8-Compiler eine andere Methode als den Java 7-Compiler auswählt.

+2

Wir hatten eine ähnliche Methode, die auch in Hunderten von Orten aufgerufen wurde. Ich ersetzte es, indem ich zusätzliche "Class clazz" Parameter einführte. Auf diese Weise können Sie eine explizite Fehlerbehandlung durchführen: entweder null zurückgeben oder eine spezifischere Ausnahme auslösen. Ich habe das manuell nur eine halbe Stunde lang ausgegeben. Ich rate Ihnen, das Gleiche zu tun. Ich weiß nicht, wie ich es automatisieren soll, daher ist dies keine Antwort auf Ihre Frage. –

+2

Es gibt eine einfache Antwort: * "ungeprüfte" Warnungen nicht unterdrücken *. Sie sagen dir bereits, dass dein Code falsch ist. Wenn Sie Ihren Code jetzt reparieren möchten, weil es zu spät ist, suchen Sie einfach nach Vorkommen von '@SuppressWarnings (" unchecked ")' ... – Holger

+0

@Holger Könnten Sie das bitte meinem Kollegen sagen, als er das im Jahr 2011 implementiert hat? Ich wusste schon vorher, dass es eine schlechte Idee war (obwohl nicht so schlecht wie jetzt in Java 8). Außerdem gibt es beim Methodenaufruf selbst keine '@ SuppressWarnings', sondern nur die Deklaration von 'getVal'. Wenn Sie diese Anmerkung hier entfernen, werden die problematischen Verwendungen nicht angezeigt. –

Antwort

2

Eine bessere Alternative wäre:

public class Test { 
    public static void main(String[] args) { 
     System.out.println(String.valueOf(getVal("xxx"))); // 7: prints the result, 8: Exception 
    } 

    @SuppressWarnings("unchecked") 
    public static <T> T getVal(T param) { 
     // do some computation based on param... 
     return param; // actual return type only depends on param so the caller knows what to expect 
    } 
} 

, die sowohl in Java 7 und Java 8.

+0

Dies ist keine Alternative, da Sie auferlegen, dass der Rückgabetyp der gleiche wie der Parameter ist (was in meinem Fall eine 'String' ist und ich kann das _easily_ das nicht ändern). Außerdem besteht die Frage darin, Fälle zu identifizieren, in denen überladene Methodenaufrufe zwischen Java 7 und 8 unterschiedlich sind, um zu vermeiden, dass sie alle ändern oder von Hand durchsucht werden. –

1

Am Ende war die Lösung getVal() zu ändern arbeiten Object zurückzukehren:

public static Object getVal(String param) { 
     // do some computation based on param... 
     return result; 
    } 

und fügen Sie eine zweite Methode hinzu, die auch die gewünschte Klasse als Parameter verwendet:

public static <T> T getVal(String param, Class<T> clazz) { 
     return clazz.cast(getVal(param)); 
    } 

dann beheben Sie alle Kompilierungsprobleme (wo Anrufer Object nicht erwartet), indem Sie den entsprechenden Klassenparameter hinzufügen.

Das Hinzufügen einer Besetzung hätte auch funktioniert, aber es hätte viele Warnungen für bedingungslose Umwandlungen verursacht. Die unbedingte Umwandlung ist tatsächlich immer noch da (durch den Parameter clazz), aber dies ermöglicht es, alle Anrufer, die eine Umwandlung benötigen, leicht zu identifizieren, wenn sie die Methode mit 2 Parametern verwenden.

Außerdem - und das ist in diesem Fall sehr spezifisch - es schien, dass die param selbst war oft das Ergebnis eines Methodenaufruf auf einige TypedParam<T> wo T der erwartete Rückgabetyp getVal() war, und die auch T Klasse enthalten ist.

Ich konnte so eine zusätzliche bequeme Methode implementieren:

public static <T> T getVal(TypedParam<T> param) { 
     return getVal(param.stringValue(), param.getValueClass()); 
    } 

und alle getVal(param.stringValue()) von nur getVal(param) ersetzt.

Diese Lösung löst nicht dem allgemeinen Fall-überladene Methodenaufrufe erkennen, die zwischen Java 7 und Java 8 unterscheiden würden - aber es löst es für Methoden, die bekannt sind, um dieses Problem zu verursachen. Und wir haben es seitdem nirgendwo anders gefunden.

Verwandte Themen