以下代码来自名为ButterKnife的Android库。我正在弄清楚它是如何工作的。
@SuppressWarnings("unchecked") // That's the point.
public <T> T castParam(Object value, String from, int fromPosition, String to, int toPosition) {
try {
return (T) value;
} catch (ClassCastException e) {
throw new IllegalStateException("Parameter #"
+ (fromPosition + 1)
+ " of method '"
+ from
+ "' was of the wrong type for parameter #"
+ (toPosition + 1)
+ " of method '"
+ to
+ "'. See cause for more info.", e);
}
}
我尝试重新创建此函数的行为:
@SuppressWarnings("unchecked")
public static <T> T cast(Object o){
try {
return (T) o;
} catch (ClassCastException e){
throw new AssertionError("Error");
}
}
用法:
Object o = new String("test");
Double d = cast(o);
但异常并非永远不会被捕获,它会在调用方法时被抛出。那是为什么?
另外,这是如何工作的?该方法如何知道要转换为什么?
答案 0 :(得分:7)
由于类型擦除,仅在编译时检查泛型类型。这样做是因为在没有破坏向后兼容性并且强制重新编译所有已经存在的库的情况下,无法在Java 5中的运行时中引入泛型。
历史悠久,当你定义一个&#34;泛型&#34;类或方法,实际代码编译为Object
,而不是您绑定方法的类型。所有类型检查都在编译时完成。
因此,您的方法代码实际上并未在return
语句中进行强制转换,因为它正在为String
返回值分配一些内容(Object
)。实际的ClassCastException由调用行返回,因为它是实际键入引用变量的位置。
答案 1 :(得分:2)
正如SJuan67所解释的那样,你不能真正使用具有泛型类型的强制类型,因为Java编译器将
如果类型参数是无界的,则将泛型类型中的所有类型参数替换为其边界或对象。因此,生成的字节码只包含普通的类,接口和方法。
有关所有泛型限制的更多信息here。
所以ButterKnife代码看起来像这样:
public Object castParam(Object paramObject, String paramString1, int paramInt1, String paramString2, int paramInt2)
{
return paramObject;
}
那么你的问题:
问:但是异常并没有被捕获,它会在调用方法时被抛出。那是为什么?
答:它甚至不在字节码中。
问:此外,这是如何工作的?该方法如何知道要转换为什么?答:没有。至少不像你认为的那样。在实践中,它会抛出ClassCastException而不是您观察到的IllegalStateException或AssertionError。 您甚至可以使用ButterKnife示例应用程序尝试它并将已知的TextView绑定到CheckBox:
@Bind(R.id.title) CheckBox title;
问:图书馆如何运作?
答:井IllegalStateException永远不会被调用,你有ClassCastException。为什么我不太确定。但是,由于ButterKnife生成代码,因此可以防止编译错误。
例如:
public interface Some {
}
public static void weWantSome(Some d) {
}
public static void test() {
String o = "test";
weWantSome((Some)o); //<-- compile error
weWantSome(Main.<Some>cast(o)); //<-- runtime error
}
这就是为什么在前面的示例中代码编译但不运行。