Java泛型基于返回类型转换?

Gre*_*ack 5 java generics casting wildcard

以下代码来自名为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);
    }
  }
Run Code Online (Sandbox Code Playgroud)

我试图重新创建此函数的行为:

  @SuppressWarnings("unchecked")
    public static <T> T cast(Object o){
        try {
            return (T) o;
        } catch (ClassCastException e){
            throw new AssertionError("Error");
        }
    }
Run Code Online (Sandbox Code Playgroud)

用法:

Object o = new String("test");
Double d = cast(o);
Run Code Online (Sandbox Code Playgroud)

但是异常并没有被捕获,它会在调用方法时被抛出.这是为什么?

另外,这是如何工作的?该方法如何知道要转换为什么?

SJu*_*n76 7

由于类型擦除,仅在编译时检查泛型类型.这样做是因为没有办法在Java 5中的运行时中引入泛型,而不会破坏向后兼容性并强制重新编译所有已存在的库.

历史悠久,当您定义"通用"类或方法时,实际代码将被编译为Object而不是您绑定方法的类型.所有类型检查都在编译时完成.

因此,您的方法代码实际上并没有在return语句中进行强制转换,因为它将某些内容(a String)分配给Object返回值.实际的ClassCastException由调用行返回,因为它是实际键入引用变量的位置.


djo*_*djo 2

正如 SJuan67 所解释的,你不能像 Java 编译器那样真正使用泛型类型的强制转换

如果类型参数无界,则将泛型类型中的所有类型参数替换为其边界或对象。因此,生成的字节码仅包含普通的类、接口和方法。

有关所有仿制药限制的更多信息请参见此处

所以 ButterKnife 代码将如下所示:

  public Object castParam(Object paramObject, String paramString1, int paramInt1, String paramString2, int paramInt2)
  {
    return paramObject;
  }
Run Code Online (Sandbox Code Playgroud)

所以对于你的问题:

问:但是异常并不是永远不会被捕获,它会在调用方法时抛出。这是为什么?

答:嗯,它甚至不在字节码中。

问:另外,这具体是如何工作的?该方法如何知道要转换到什么?

答:没有。至少不会像你想象的那样。实际上,它会抛出 ClassCastException,而不是您观察到的 IllegalStateException 或 AssertionError。您甚至可以使用 ButterKnife 示例应用程序进行尝试,并将已知的 TextView 绑定到 CheckBox:

@Bind(R.id.title) CheckBox title;
Run Code Online (Sandbox Code Playgroud)

问:那么图书馆如何运作?

答:好吧,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
} 
Run Code Online (Sandbox Code Playgroud)

这就是为什么前面的示例代码可以编译但不能运行。