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

Ale*_*yak 104 java generics casting compiler-warnings

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

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

结果Class.caststatic_castC++不同.它更像是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 );
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

所以,这些是我的问题.

  1. 应该Class.cast()放逐到仿制药土地?它有很多合法的用途.
  2. 编译器在Class.cast()使用时是否会产生编译错误,并且在编译时可以确定非法条件?
  3. Java是否应该提供类似于C++的转换操作符作为语言结构?

sfu*_*ger 112

我只是习惯于Class.cast(Object)避免在"仿制品土地"中发出警告.我经常看到方法做这样的事情:

@SuppressWarnings("unchecked")
<T> T doSomething() {
    Object o;
    // snip
    return (T) o;
}
Run Code Online (Sandbox Code Playgroud)

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

<T> T doSomething(Class<T> cls) {
    Object o;
    // snip
    return cls.cast(o);
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 我认为在这种情况下正确的方法是先做一个检查实例,如下所示:if(cls.isInstance(o)){return cls.cast(o); 当然,除非你确定类型是正确的. (7认同)
  • 但是,当您在编译时没有收到任何警告时,仍然可以在调用cls.cast(o)时获取ClassCastExceptions.我更喜欢第一个变体,但是当我需要例如如果我不能进行转换时返回null,我将使用第二个变体.在这种情况下,我会将cls.cast(o)包装在try catch块中.我也同意你的看法,当编译器无法推断T时这也更好.感谢您的回复. (2认同)

not*_*oop 19

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

在任何情况下,Class.cast()主要应该在Class通过反射检索令牌时使用.写作更加惯用

MyObject myObject = (MyObject) object
Run Code Online (Sandbox Code Playgroud)

而不是

MyObject myObject = MyObject.class.cast(object)
Run Code Online (Sandbox Code Playgroud)

编辑:编译时的错误

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


cle*_*tus 16

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

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

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

  • @Alexander Pogrebnyak:不要那样做!`Bar.class.cast(foo)`明确告诉编译器你想在运行时进行转换.如果你想要对演员表的有效性进行编译时检查,你唯一的选择就是做`(Bar)foo`样式演员. (5认同)

Joa*_*uer 13

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

并不相似reinterpret_cast,因为它不会允许你在运行时打破类型系统,而不是普通的转换(即你可以"打破"泛型类型参数,但不能"打破""真正的"类型).

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

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


Cal*_*leb 6

通常,强制转换运算符优于 Class#cast 方法,因为它更简洁,并且可以由编译器分析以找出代码中明显的问题。

Class#cast 负责在运行时而不是在编译期间进行类型检查。

Class#cast 当然有一些用例,特别是在反射操作方面。

自从 lambda 进入 java 以来,例如,如果我正在使用抽象类型,我个人喜欢将 Class#cast 与集合/流 API 一起使用。

Dog findMyDog(String name, Breed breed) {
    return lostAnimals.stream()
                      .filter(Dog.class::isInstance)
                      .map(Dog.class::cast)
                      .filter(dog -> dog.getName().equalsIgnoreCase(name))
                      .filter(dog -> dog.getBreed() == breed)
                      .findFirst()
                      .orElse(null);
}
Run Code Online (Sandbox Code Playgroud)