Я написал метод замены View.findViewById(int id)
, но получил предупреждение:
Есть ли проблема в этом методе? Или как этого избежать?
Я написал метод замены View.findViewById(int id)
, но получил предупреждение:
Есть ли проблема в этом методе? Или как этого избежать?
Я считаю, что это причина предупреждения: Java допускает понижающее приведение, то есть приведение объекта типа X к подклассу X. Однако это требует проверки во время выполнения. Например:
Object x1 = <... something that returns an object that may be a String ...>;
String x2 = (String)x1;
Актерский состав попытается относиться к x1
как к String
. Но x1
может быть любым Object
. Это может быть, а может и не быть String
. Приведение работает, только если x1
на самом деле является String
; в противном случае вы получите ClassCastException
во время выполнения.
Проблема в вашем коде заключается в том, что T
является подклассом View
, а это означает, что приведение типов обычно приводит к выполнению такой же проверки во время выполнения. Однако из-за стирания типа программа фактически не имеет информации о классе T
в этом месте кода, что означает, что она не может выполнить проверку. Таким образом, вы получаете предупреждение о том, что программа использует непроверенную операцию. Программа не может гарантировать, что при возвращении этого метода возвращенный объект действительно будет экземпляром класса T
.
Я попробовал несколько тестовых примеров и обнаружил, что проверка часто выполняется для инструкции, которая вызывает универсальный метод. Предположим, что View2
расширяет View
, и вы хотите использовать find
таким образом, чтобы возвращалось View2
. Потом:
View2 v = YourClass.<View2>find(x, id);
Если объект, найденный findViewById
, на самом деле не View2
, вы получите ClassCastException
. Но:
View v = YourClass.<View2>find(x, id);
Предположим, что findViewById
возвращает какое-то другое представление. Основываясь на моих тестах, это не вызовет исключения, даже если параметр типа равен View2
; так как это назначается в View
, который будет работать нормально, если результатом является какое-то другое представление, проверка на View2
никогда не происходит.
String s = YourClass.<View2>find(x, id).toString();
Предположим, что findViewById
возвращает какое-то другое представление. Когда я пробовал это, я думал, что это не вызовет исключения, потому что другое представление также будет иметь toString()
(как и все Object
s). Но это бросило ClassCastException
.
Я не знаю, есть ли хороший способ это исправить. Одна из возможностей — добавить третий параметр к find
для обозначения класса T
и использовать его метод cast
:
public static <T extends View> T find(View view, int id, Class<T> theClass) {
return theClass.cast(view.findViewById(id));
}
View v = YourClass.find(x, id, View2.class);
Это работает — выдается исключение из метода cast()
, если findViewById
возвращает неправильный класс. Однако добавление третьего параметра для каждого использования может оказаться не очень привлекательным, даже если это позволит исключить явный параметр универсального типа из вызова.
Я думаю, что это тот случай, когда можно игнорировать предупреждение и использовать @SuppressWarnings("unchecked")
, чтобы предотвратить появление предупреждения, если вы согласны с возможностью того, что иногда программа может продолжить работу вместо того, чтобы генерировать исключение, когда findViewById
возвращает неправильный вид зрения.
(Отказ от ответственности: Мои тесты проводились с использованием Java 8. Я не использовал ничего, что было бы недопустимо в Java 7. Но если Android еще не полностью реализовал Java 7, некоторые из моих написал, может ошибся)