java 泛型方法警告 "Unchecked cast"
java generic method warning "Unchecked cast"
我写了一个方法来替代View.findViewById(int id)
,但是我得到了一个警告:
这个方法有问题吗?或者如何避免?
我相信这是警告的原因:Java 允许向下转换,即将类型 X 的对象转换为 X 的子class。但是,这需要检查运行。例如:
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
的子 class,这意味着转换通常会导致在运行时执行相同类型的检查.但是,由于类型擦除,程序此时在代码中实际上并没有关于 T
的 class 的信息,这意味着它无法执行检查。因此,您会收到有关使用未经检查的操作的程序的警告。程序不能保证,当这个方法returns时,返回的对象实际上是class T
.
的一个实例
我尝试了一些测试用例,发现检查经常发生在调用泛型方法的语句上。假设 View2
扩展了 View
,并且您想以 returns 和 View2
的方式使用 find
。那么:
View2 v = YourClass.<View2>find(x, id);
如果 findViewById
找到的对象实际上不是 View2
,您将得到 ClassCastException
。但是:
View v = YourClass.<View2>find(x, id);
假设findViewById
returns 一些其他视图。根据我的测试,这不会引发异常,即使类型参数是 View2
;因为它被分配到 View
,如果结果是其他视图,它会工作正常,所以不会检查 View2
。
String s = YourClass.<View2>find(x, id).toString();
假设findViewById
returns 一些其他视图。当我尝试这个时,我认为它不会抛出异常,因为另一个视图也会有 toString()
(就像所有 Object
一样)。但这确实抛出 ClassCastException
。
不知道有没有好的办法解决。一种可能是在 find
中添加第三个参数来表示 T
的 class,并使用它的 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);
这行得通——如果 findViewById
returns 错误 class,它会从 cast()
方法中抛出异常。但是,为每次使用添加第三个参数可能并不吸引人,即使它可以让您从调用中消除显式泛型类型参数。
我认为在这种情况下可以忽略警告,并使用 @SuppressWarnings("unchecked")
阻止警告出现,如果您同意有时程序可能会继续而不是当 findViewById
returns 错误的视图类型时抛出异常。
(免责声明: 我的测试是使用 Java 8 进行的。我没有使用任何在 Java 7 中无效的东西。但是如果 Android 仍然没有完全实现 Java 7,我写的一些内容可能是错误的。)
我写了一个方法来替代View.findViewById(int id)
,但是我得到了一个警告:
这个方法有问题吗?或者如何避免?
我相信这是警告的原因:Java 允许向下转换,即将类型 X 的对象转换为 X 的子class。但是,这需要检查运行。例如:
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
的子 class,这意味着转换通常会导致在运行时执行相同类型的检查.但是,由于类型擦除,程序此时在代码中实际上并没有关于 T
的 class 的信息,这意味着它无法执行检查。因此,您会收到有关使用未经检查的操作的程序的警告。程序不能保证,当这个方法returns时,返回的对象实际上是class T
.
我尝试了一些测试用例,发现检查经常发生在调用泛型方法的语句上。假设 View2
扩展了 View
,并且您想以 returns 和 View2
的方式使用 find
。那么:
View2 v = YourClass.<View2>find(x, id);
如果 findViewById
找到的对象实际上不是 View2
,您将得到 ClassCastException
。但是:
View v = YourClass.<View2>find(x, id);
假设findViewById
returns 一些其他视图。根据我的测试,这不会引发异常,即使类型参数是 View2
;因为它被分配到 View
,如果结果是其他视图,它会工作正常,所以不会检查 View2
。
String s = YourClass.<View2>find(x, id).toString();
假设findViewById
returns 一些其他视图。当我尝试这个时,我认为它不会抛出异常,因为另一个视图也会有 toString()
(就像所有 Object
一样)。但这确实抛出 ClassCastException
。
不知道有没有好的办法解决。一种可能是在 find
中添加第三个参数来表示 T
的 class,并使用它的 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);
这行得通——如果 findViewById
returns 错误 class,它会从 cast()
方法中抛出异常。但是,为每次使用添加第三个参数可能并不吸引人,即使它可以让您从调用中消除显式泛型类型参数。
我认为在这种情况下可以忽略警告,并使用 @SuppressWarnings("unchecked")
阻止警告出现,如果您同意有时程序可能会继续而不是当 findViewById
returns 错误的视图类型时抛出异常。
(免责声明: 我的测试是使用 Java 8 进行的。我没有使用任何在 Java 7 中无效的东西。但是如果 Android 仍然没有完全实现 Java 7,我写的一些内容可能是错误的。)