【问题标题】:Casting a generic class. (cast) vs Class.cast()铸造一个泛型类。 (cast) 与 Class.cast()
【发布时间】:2016-03-08 06:47:49
【问题描述】:

我搜索了我的用例并找到了一些有趣的答案,但它们并不像我需要的那样合适。这样做的适当方法是什么:

@SuppressWarnings("unchecked")
public <T> T newInstance(String name) throws ClassCastException, InstantiationException, IllegalAccessException, ClassNotFoundException {
    return (T) loadClass(name).newInstance();
}

或有点不同:

public <T> T newInstance(String name, Class<T> c) throws ClassCastException, InstantiationException, IllegalAccessException, ClassNotFoundException {
    return c.cast(loadClass(name).newInstance());
}

我认为这两种方法都是一样的。从我的角度来看,方法1更好,因为参数更少。两者都抛出ClassCastException,这对我来说很好。确实,@SuppressWarnings("unchecked") 注释不好。

谁能告诉我一种方法相对于另一种方法是否有任何优势?

编辑:Jon Skeet's answer 是正确的。下面的 sn-p 可以提供额外的说明。

public class Test {
    public static void main(String[] args) {
        Object o = new Object();
        // Exception at c.cast(o)
        Test.<String>realCast(o, String.class);
    }

    private static <T> T realCast(Object o, Class<T> c) {
        return c.cast(o);
    }
}

当o 无法转换为c 时,使用realCast() 会产生异常。相比之下,fakeCast() 只承诺该方法的结果是T 的类型。

【问题讨论】:

    标签: java generics casting


    【解决方案1】:

    我认为这两种方法都是一样的。

    不,他们没有。因为在执行时,由于type erasure,第一个代码不知道T 的类型。这意味着强制转换在方法中基本上什么都不做。 调用 代码可能会隐式转换为它指定的任何T,但如果调用者是另一个泛型方法(这里使用T 作为另一个类型参数),即使这样也不会想要.

    这是一个简单的例子:

    public class Test {
        public static void main(String[] args) {
            Object o = new Object();
            // No exception
            Test.<String>fakeCast(o);
            // Exception at the point of assignment:
            // the code is effectively String x = (String) ...;
            String x = Test.<String>fakeCast(o);
        }
    
        private static <T> T fakeCast(Object o) {
            return (T) o;
        }
    }
    

    第二个代码以Class&lt;T&gt; 的形式知道T 的类型,因此它可以在执行时执行真正的强制转换,正是你正在执行它。

    【讨论】:

    • 我想我明白了。 fakeCast() 对不在方法内的赋值进行强制转换,而 realCast() 在返回之前在方法中进行强制转换。
    • @mr.b: 不,fakeCast() 本身不会完全。方法中的强制转换绝对没有任何作用。只是当一个方法返回T 时,它实际上只是返回Object 并且调用 代码如果它实际上使用返回值作为T 进行转换。请注意第一次调用不会导致异常。
    • 是的,我没有很好地表达我的意思。你说的对。非常感谢。
    【解决方案2】:

    Jon Skeet 很好地解释了答案。我想在这里添加一个示例,以便可以清楚地观察到差异

    类.cast()

    public class Test{
          public static void main(String[] args){
                Object o = new Object();
                Test.castMethod(o, String.class); //Exception is thrown here
          }
    
          public static <T> T castMethod (Object o, Class<T> tClass){
                 return tClass.cast(o)
          }
    }
    

    输出:

    Exception in thread "main" java.lang.ClassCastException: Cannot cast java.lang.Object to java.lang.String
    at java.base/java.lang.Class.cast
    at com.test.Test.castMethod
    

    将Object.class 对象向下转换为String.class 在这里是非法的,因为它们不兼容。

    通过使用Class.cast(), 转换确实发生在castMethod() 并因此抛出ClassCastException。 这就是Jon 所说的真正的转换 的含义。


    转换运算符

    public class Test{
         public static void main(String[] args){
              Object o = new Object();
              Test.<String>castMethod(o); //No Exception
              String x = Test.<String>castMethod(o); //Exception is thrown here
         }
         public static <T> T castMethod(Object o){
              return (T) o;
         }
    }
    

    输出:

    Exception in thread "main" java.lang.ClassCastException: java.base/java.lang.Object cannot be cast to java.base/java.lang.String
    at com.test.Test.main
    

    从输出中,您可以看到ClassCastException 在main() 处被抛出,这与在castMethod() 处抛出异常的 Class.cast() 不同。这就是 Jon 将其命名为 Fake Casting 的原因,因为在将 castMethod 的结果分配给 String 变量时实际完成了转换。如果调用castMethod 并忽略结果,则根本不会出现异常。

    另外,return (T) o 会给你一个来自 linter 的丑陋的 Unchecked Cast 警告

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-17
      相关资源
      最近更新 更多