Java Class.cast()与强制转换运算符

问题描述 投票:96回答:7

在我的C ++时代被教授关于C风格的演员的邪恶,我很高兴首先发现在Java 5中java.lang.Class已经获得了cast方法。

我认为最后我们有一个处理铸造的OO方式。

结果Class.cast与C ++中的static_cast不同。它更像是reinterpret_cast。它不会在预期的位置生成编译错误,而是会延迟到运行时。这是一个简单的测试用例,用于演示不同的行为。

package test;

import static org.junit.Assert.assertTrue;

import org.junit.Test;


public class TestCast
{
    static final class Foo
    {
    }

    static class Bar
    {
    }

    static final class BarSubclass
        extends Bar
    {
    }

    @Test
    public void test ( )
    {
        final Foo foo = new Foo( );
        final Bar bar = new Bar( );
        final BarSubclass bar_subclass = new BarSubclass( );

        {
            final Bar bar_ref = bar;
        }

        {
            // Compilation error
            final Bar bar_ref = foo;
        }
        {
            // Compilation error
            final Bar bar_ref = (Bar) foo;
        }

        try
        {
            // !!! Compiles fine, runtime exception
            Bar.class.cast( foo );
        }
        catch ( final ClassCastException ex )
        {
            assertTrue( true );
        }

        {
            final Bar bar_ref = bar_subclass;
        }

        try
        {
            // Compiles fine, runtime exception, equivalent of C++ dynamic_cast
            final BarSubclass bar_subclass_ref = (BarSubclass) bar;
        }
        catch ( final ClassCastException ex )
        {
            assertTrue( true );
        }
    }
}

所以,这些是我的问题。

  1. Class.cast()应该被放逐到仿制药土地吗?它有很多合法的用途。
  2. 当使用Class.cast()并且编译时可以确定非法条件时,编译器是否应该生成编译错误?
  3. Java是否应该提供类似于C ++的转换操作符作为语言结构?
java generics casting compiler-warnings
7个回答
102
投票

我只使用过Class.cast(Object)来避免“仿制土地”中的警告。我经常看到方法做这样的事情:

@SuppressWarnings("unchecked")
<T> T doSomething() {
    Object o;
    // snip
    return (T) o;
}

通常最好通过以下方式替换它:

<T> T doSomething(Class<T> cls) {
    Object o;
    // snip
    return cls.cast(o);
}

这是我遇到的Class.cast(Object)唯一的用例。

关于编译器警告:我怀疑Class.cast(Object)对编译器并不特殊。它可以在静态使用时进行优化(即Foo.class.cast(o)而不是cls.cast(o)),但我从未见过任何人使用它 - 这使得在编译器中构建这种优化的努力有些毫无价值。


17
投票

首先,你强烈劝阻几乎不做任何演员,所以你应该尽可能地限制它!您将失去Java编译时强类型功能的好处。

在任何情况下,Class.cast()应该主要用于通过反射检索Class标记。写作更加惯用

MyObject myObject = (MyObject) object

而不是

MyObject myObject = MyObject.class.cast(object)

编辑:编译时的错误

总而言之,Java仅在运行时执行转换检查。但是,如果编译器可以证明此类强制转换永远不会成功(例如,将类强制转换为不是超类型的另一个类并将最终类类型转换为不在其类型层次结构中的类/接口),则编译器可以发出错误。在这里,因为FooBar是不在每个其他层次结构中的类,所以演员阵容永远不会成功。


16
投票

尝试翻译语言之间的结构和概念总是存在问题并且经常会产生误导。铸造也不例外。特别是因为Java是一种动态语言而且C ++有些不同。

无论如何操作,所有Java中的转换都是在运行时完成的。类型信息在运行时保存。 C ++有点混合。您可以将C ++中的结构转换为另一个结构,它只是对表示这些结构的字节的重新解释。 Java不会那样工作。

Java和C ++中的泛型也是截然不同的。不要过分关注你如何用Java做C ++事情。您需要学习如何以Java方式执行操作。


10
投票

Class.cast()很少用于Java代码。如果它被使用,则通常使用仅在运行时已知的类型(即,通过它们各自的Class对象和某些类型参数)。它只对使用泛型的代码非常有用(这也是之前没有介绍过的原因)。

它与reinterpret_cast不相似,因为它不会允许你在运行时打破类型系统,而不是正常的转换(即你可以“打破”泛型类型参数,但不能“打破”“真正的”类型) 。

C风格的强制转换操作符的弊端通常不适用于Java。看起来像C风格的强制转换的Java代码最类似于带有Java引用类型的dynamic_cast<>()(请记住:Java具有运行时类型信息)。

通常将C ++强制转换操作符与Java转换进行比较非常困难,因为在Java中,您只能转换引用,并且对象不会发生任何转换(只能使用此语法转换原始值)。


3
投票

C ++和Java是不同的语言。

Java C风格的强制转换操作符比C / C ++版本更受限制。实际上,Java转换类似于C ++ dynamic_cast,如果您拥有的对象无法转换为新类,您将获得运行时(或者代码中有足够的信息编译时)异常。因此,不使用C类型转换的C ++思想在Java中不是一个好主意


0
投票

除了最常提到的删除丑陋的强制转换警告之外,Class.cast是运行时强制转换,主要用于通用转换,因为通用信息将在运行时被删除,而某些通用将如何被视为对象,这导致不会抛出早期的ClassCastException。

例如,serviceLoder在创建对象时使用此技巧,检查S p = service.cast(c.newInstance());当S P =(S)c.newInstance()时,这将抛出一个类强制转换异常;将不会并且可能会显示警告'类型安全:未选中从对象转换为S'。(与对象P =(对象)相同c.newInstance();)

- 只是它检查铸造对象是否为铸造类的实例,然后它将使用强制转换操作符通过抑制它来强制转换和隐藏警告。

动态强制转换的java实现:

@SuppressWarnings("unchecked")
public T cast(Object obj) {
    if (obj != null && !isInstance(obj))
        throw new ClassCastException(cannotCastMsg(obj));
    return (T) obj;
}




    private S nextService() {
        if (!hasNextService())
            throw new NoSuchElementException();
        String cn = nextName;
        nextName = null;
        Class<?> c = null;
        try {
            c = Class.forName(cn, false, loader);
        } catch (ClassNotFoundException x) {
            fail(service,
                 "Provider " + cn + " not found");
        }
        if (!service.isAssignableFrom(c)) {
            fail(service,
                 "Provider " + cn  + " not a subtype");
        }
        try {
            S p = service.cast(c.newInstance());
            providers.put(cn, p);
            return p;
        } catch (Throwable x) {
            fail(service,
                 "Provider " + cn + " could not be instantiated",
                 x);
        }
        throw new Error();          // This cannot happen
    }

0
投票

就个人而言,我之前使用过它来构建一个JSON到POJO转换器。在使用函数处理的JSONObject包含数组或嵌套的JSONObjects(暗示此处的数据不是基本类型或String)的情况下,我尝试以这种方式使用class.cast()调用setter方法:

public static Object convertResponse(Class<?> clazz, JSONObject readResultObject) {
    ...
    for(Method m : clazz.getMethods()) {
        if(!m.isAnnotationPresent(convertResultIgnore.class) && 
            m.getName().toLowerCase().startsWith("set")) {
        ...
        m.invoke(returnObject,  m.getParameters()[0].getClass().cast(convertResponse(m.getParameters()[0].getType(), readResultObject.getJSONObject(key))));
    }
    ...
}

不确定这是否非常有用,但如前所述,反射是我能想到的class.cast()的极少数合法用例之一,至少你现在有另一个例子。

© www.soinside.com 2019 - 2024. All rights reserved.