Как обнаружить неоднозначные вызовы методов, которые вызовут ClassCastException в Java 8?
В настоящее время мы переносим приложение с Java 7 на Java 8. После устранения некоторых проблем с компиляцией я наткнулся на проблему, похожую на следующий вопрос: ClassCast Error: Java 7 vs Java 8.
Подведем итог, вот пример кода, который показывает проблему:
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
}
}
Идея заключалась в том, что мы запустим, что вызывающий абонент знает ожидаемый тип, и это позволит избежать явного приведения (я не говорю, что это была хорошая идея...). Во многих случаях вызывающий абонент просто ожидает Object
, поэтому никакого неявного приведения не было.
Как указано в вышеприведенном вопросе, пример String.valueOf
отлично работал в Java 7, потому что не было вывода типа, поэтому предполагалось Object
. Теперь в Java 8 компилятор выбирает наиболее специфический тип (здесь char[]
), который вызывает ClastCastException
во время выполнения.
Проблема заключается в том, что у нас есть около 350 вызовов этого метода getVal
. Есть ли способ обнаружить перегруженные вызовы методов, которые будут отличаться между Java 7 и Java 8? I.E. определить, когда компилятор Java 8 будет выбирать другой метод из компилятора Java 7.
Ответы
Ответ 1
Лучшей альтернативой может быть:
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
}
}
который будет работать как на Java 7, так и на Java 8.
Ответ 2
В итоге решение заключалось в изменении getVal()
для возврата Object
:
public static Object getVal(String param) {
// do some computation based on param...
return result;
}
и добавьте второй метод, который также примет желаемый класс в качестве параметра:
public static <T> T getVal(String param, Class<T> clazz) {
return clazz.cast(getVal(param));
}
затем исправить все проблемы компиляции (когда вызывающий не ожидал Object
), добавив соответствующий параметр класса.
Добавление каста также сработало бы, но это вызвало бы множество предупреждений о безусловных переводах. Безусловный листинг на самом деле все еще существует (с помощью параметра clazz
), но это позволяет легко идентифицировать всех вызывающих абонентов, которым требуется приведение, поскольку они используют метод с двумя параметрами.
Дополнительно - и это очень специфично для этого случая - оказалось, что сам param
был часто результатом вызова метода на некотором TypedParam<T>
, где T
был ожидаемым типом возвращаемого значения getVal()
, и который также содержал класс T.
Я мог бы таким образом реализовать дополнительный метод удобства:
public static <T> T getVal(TypedParam<T> param) {
return getVal(param.stringValue(), param.getValueClass());
}
и заменил все getVal(param.stringValue())
на просто getVal(param)
.
Это решение не разрешает общий случай - обнаруживает перегруженные вызовы методов, которые будут отличаться между Java 7 и Java 8, но он разрешает его для методов, которые, как известно, вызывают эту проблему. И с тех пор мы не нашли его в другом месте.