简体   繁体   English

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

[英]Java generics cast based on return type?

The following code is from an Android library called ButterKnife. 以下代码来自一个名为ButterKnife的Android库。 I'm figuring out how it works. 我正在弄清楚它是如何工作的。

@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);
    }
  }

I tried to recreate the behaviour of this function: 我试图重新创建此函数的行为:

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

And usage: 和用法:

Object o = new String("test");
Double d = cast(o);

But the exception is not never caught, it gets thrown at the line when the method is called. 但是异常并非永远不会被捕获,它在调用该方法时会抛出该行。 Why is that? 这是为什么?

Also, how does this work exactly? 另外,这究竟如何工作? How does the method know what to cast to? 该方法如何知道要转换为什么?

Generics types are checked at compile time only, due to type erasure. 由于类型擦除,泛型类型仅在编译时检查。 This is done because there was no way to introduce generics in the runtime in Java 5 without breaking backwards compatibility and forcing to recompile all the already existing libraries. 这样做是因为无法在Java 5的运行时中引入泛型而不破坏向后兼容性并强制重新编译所有已经存在的库。

Long history short, when you define a "generic" class or method, the actual code is compiled as Object instead of the type you are binding the method. 历史悠久,当您定义“通用”类或方法时,实际代码将编译为Object而不是您要绑定该方法的类型。 All the checks of types are done at compile time. 所有类型检查均在编译时完成。

So, your method code is not actually doing a cast in the return statement, since it is assigning something (a String ) to an Object return value. 因此,您的方法代码实际上并未在return语句中进行强制转换,因为它正在为Object返回值分配某些内容( String )。 The actual ClassCastException is returned by the calling line because it is the place when the reference variable is actually typed. 实际的ClassCastException由调用行返回,因为它是实际键入引用变量的地方。

As SJuan67 explained, you cannot really use casts with generic types as Java compiler will 正如SJuan67所述,您不能像Java编译器那样真正使用具有泛型类型的强制类型转换

Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. 如果类型参数不受限制,则将通用类型中的所有类型参数替换为其边界或对象。 The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods. 因此,产生的字节码仅包含普通的类,接口和方法。

More info on all generics restrictions here . 有关所有仿制药限制的更多信息,请参见此处

So ButterKnife code will look like this: 因此,ButterKnife代码如下所示:

  public Object castParam(Object paramObject, String paramString1, int paramInt1, String paramString2, int paramInt2)
  {
    return paramObject;
  }

So to your questions: 所以对你的问题:

Q: But the exception is not never caught, it gets thrown at the line when the method is called. 问:但是并非永远不会捕获异常,在调用方法时,它会抛出异常。 Why is that? 这是为什么?

A: Well its not even in the bytecode. 答:嗯,甚至没有在字节码中。

Q: Also, how does this work exactly? 问:此外,这是如何工作的? How does the method know what to cast to? 该方法如何知道要转换为什么?

A: It doesn't. 答:不是。 At least not like you think it will. 至少不像您想的那样。 In practice it will throw ClassCastException not IllegalStateException or AssertionError as you observed. 在实践中,它将抛出ClassCastException而不是您观察到的IllegalStateException或AssertionError。 You can even try it with ButterKnife sample app and Bind a known TextView to CheckBox: 您甚至可以使用ButterKnife示例应用程序尝试将其绑定到CheckBox:

@Bind(R.id.title) CheckBox title;

Q: How does the library work then? 问:图书馆如何工作?

A: Well IllegalStateException is just never called and you have ClassCastException. 答:好吧,永远不会调用IllegalStateException,而您有ClassCastException。 Why it is like that I an not really sure. 我不确定为什么会这样。 However as ButterKnife generates code this could be intended to prevent from compile errors. 但是,当ButterKnife生成代码时,这可能是为了防止编译错误。

for example: 例如:

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
} 

Which is why in the previous example code compiles but does not run. 这就是为什么在前面的示例代码中编译但无法运行的原因。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM