【问题标题】:Why would I use java.lang.Class.cast [duplicate]为什么我要使用 java.lang.Class.cast [重复]
【发布时间】:2011-10-26 08:38:20
【问题描述】:

我最近偶然发现了一段这样的代码:

Object o = .. ;
Foo foo = Foo.class.cast(o);

实际上我什至不知道 java.lang.Class 有一个 cast 方法,所以我查看了文档,从我收集到的内容中,它只是对 Class 对象所代表的类进行了转换。所以上面的代码大致相当于

Object o = ..;
Foo foo = (Foo)o;

所以我想知道,为什么我要使用 cast 方法而不是简单地使用“旧方法”进行强制转换。有没有人举个例子说明使用 cast 方法比使用简单的 cast 更有效?

【问题讨论】:

    标签: java generics language-construct


    【解决方案1】:

    我不认为它经常像你展示的那样被使用。我见过的最常见的用途是使用泛型的人试图做与此等价的事情:

    public static <T extends Number> T castToNumber(Object o) {
        return (T)o;
    }
    

    由于类型擦除,这并没有真正做任何有用的事情。

    虽然这可行,并且是类型安全的(模 ClassCastExceptions):

    public static <T extends Number> T castToNumber(Object o, Class<T> clazz) {
        return clazz.cast(o);
    }
    

    编辑:google guava 的几个使用示例:

    【讨论】:

    • 这仍然等价于(Number) o,因为转换对对象的实际类型没有影响。
    • 并非如此,但请记住这是一个简化的示例,通常oclazz 将在运行时确定,而不是硬编码。 Integer x = castToNumber(o, Integer.class) 在这里有效,但 Integer x = (Number)o 不会。也许我应该在示例中省略 Number 位?也许castToNumber 是一个错误的方法名称选择?
    • 我想说的是,您仍然需要在作业的左侧,您需要在其中声明一个明确的类。所以你总是可以简单地写T x = (T) o;
    • 我的意思是T 是您可能想到的任何类的通用占位符,而不是泛型构造。就这样的真实代码而言:Object o = new Object(); Integer i = (Integer) o; 显然这段代码会在运行时抛出一个ClassCastException,但它不会显示编译器警告。
    • 我暂时误解了您的回答,但我想我现在明白了。您的评论对于客户端代码是合理的,但如果您正在编写一个库,则并不总是清楚确切的用途是什么。我从番石榴库中添加了一些具体用途。
    【解决方案2】:

    在 Java 中,给猫剥皮的方法通常不止一种。在您拥有框架代码的情况下,此类功能可能很有用。想象一个方法,它接受一个 Class 对象实例和一个 Object 实例,并将 Object 情况作为类返回:

    public static void doSomething(Class<? extends SomeBaseClass> whatToCastAs,Object o)
        {
            SomeBaseClass castObj =  whatToCastAs.cast(o);
            castObj.doSomething();
        }
    

    一般来说,使用更简单的转换,除非它不够。

    【讨论】:

    • 你在开玩笑吧?为什么不直接写((SomeBaseClass) o).doSomething();
    • @nfechner,因为你不能总是那样做。我想最好的例子是(部分)JDBC public T unwrap(Class iface) 方法的实现。在那里你可以做类似的事情:return iface.cast(this);
    • (CastTarget)o 是编译时间,foo.getClass().cast(0) 是运行时间
    【解决方案3】:

    在某些情况下,您只知道在运行时将对象转换为的类型,这时您必须使用 cast 方法。

    【讨论】:

      【解决方案4】:

      绝对没有理由写Foo.class.cast(o),相当于(Foo)o

      一般来说,如果X是一个可具体化的类型,而Class&lt;X&gt; clazz,那么clazz.cast(o)(X)o是一样的。

      如果所有类型都是可具体化的,那么方法Class.cast()因此是多余且无用的。

      不幸的是,由于当前版本的 Java 中的擦除,并非所有类型都是可具体化的。例如,类型变量是不可具体化的。

      如果T 是类型变量,则不选中强制转换(T)o,因为在运行时,JVM 不知道T 的确切类型,JVM 无法测试o 是否真的是T 类型。演员可能被错误地允许,这可能会在以后引发问题。

      这不是一个大问题;通常当程序员做(T)o 时,他已经推断出强制转换是安全的,并且不会在运行时引起任何问题。演员表由应用逻辑检查

      假设Class&lt;T&gt; clazz 在转换时可用,那么我们确实知道T 在运行时是什么;我们可以添加额外的运行时检查以确保 o 确实是 T

      check clazz.isInstance(o);
      (T)o;
      

      这基本上就是Class.cast() 所做的。

      我们永远不会期望强制转换在任何情况下都会失败,因此在正确实现的应用程序中,检查clazz.isInstance(o) 必须始终成功,因此clazz.cast(o) 等同于(T)o - 再次假设代码是正确的。

      如果可以证明代码是正确的并且演员表是安全的,出于性能原因,人们可能更喜欢(T)o 而不是clazz.cast(o)。在另一个答案中提出的MutableClassToInstanceMap 的示例中,我们可以清楚地看到演员表是安全的,因此简单的(T)o 就足够了。

      【讨论】:

      • “如果所有类型都是可具体化的,那么方法 Class.cast() 是多余的和无用的。”不对。 Class.cast() 当你在编译时不知道类,并且在运行时只有一个类对象时是必要的
      • @newacct 如果 java 是完全可具体化的,则运行时知道T 的实际类型,因此它可以检查(T)o 以确保o 确实属于T 类型。那么就不需要Class&lt;T&gt;.cast(o)了。
      【解决方案5】:

      class.cast 是为泛型类型设计的。

      当你用泛型参数 T 构造一个类时,你可以传入一个 班级。然后,您可以使用静态和动态进行演员表 检查,(T)没有给你。它也不会产生未经检查的 警告,因为它已被检查(此时)。

      【讨论】:

        【解决方案6】:

        常见的示例是当您从持久层检索由类对象和某些条件引用的实体集合时。返回的集合可能包含未经检查的对象,因此如果您只是将其转换为指向 G_H,您将在此时抛出 Cast 异常,而不是在访问值时。

        这方面的一个示例是,当您从返回未经检查的集合的 DAO 中检索集合并在您的服务上对其进行迭代时,这种情况可能会导致 ClassCastException。

        解决它的一种方法,因为您有所需的类,并且未检查的集合对其进行迭代并将其强制转换到 DAO 中,从而将集合转换为已检查的集合,然后将其返回。

        【讨论】:

          【解决方案7】:

          因为你可能有这样的东西:

          Number fooMethod(Class<? extends Number> clazz) {
              return clazz.cast(var);
          }
          

          【讨论】:

            【解决方案8】:

            Java 中的“强制转换”,例如(Number)var,括号里面的东西是引用类型,实际上由两部分组成:

            • 编译时间:转换表达式的结果具有您转换为的类型的类型
            • 运行时:它插入一个检查操作,基本上是说,如果对象不是该类的实例,则抛出ClassCast Exception(如果您要转换的对象是类型变量,则该类它会检查类型变量的下限)

            要使用语法,您需要在编写代码时了解类。假设您在编译时不知道要转换为哪个类;你只有在运行时才知道。

            现在你会问,那铸造的意义何在?转换的目的不是在编译时将表达式转换为所需的类型吗?因此,如果您在编译时不知道类型,那么在编译时没有任何好处,对吧?没错,但这只是上面的第一项。您忘记了强制转换的运行时组件(上面的第二项):它根据类检查对象。

            因此,运行时强制转换(即Class.cast())的目的是检查对象是否是类的实例,如果不是,则抛出异常。它大致相当于这个但更短:

            if (!clazz.isInstance(var))
                throw new ClassCastException();
            

            有人提到Class.cast() 也有一个很好的返回类型,它基于传入的类的类型参数,但这只是编译时转换提供的编译时特性。因此,为此目的,使用Class.cast() 毫无意义。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2023-03-29
              • 1970-01-01
              • 2010-11-08
              • 1970-01-01
              • 2013-12-17
              • 2011-04-24
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多