Java Class.cast() vs. cast operator
在C ++的日子里,当我学习了有关C样式转换运算符的弊端时,我很高兴首先发现在Java 5中
我以为最终我们有了一种面向对象的处理铸造的方法。
事实证明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | 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 2 3 4 5 6 |
通常最好将其替换为:
1 2 3 4 5 |
这是我见过的
关于编译器警告:我怀疑
首先,强烈建议您不要进行任何类型的演员表转换,因此应尽可能限制它!您将失去Java的编译时强类型功能的好处。
无论如何,在通过反射检索
1 | MyObject myObject = (MyObject) object |
而不是
1 | MyObject myObject = MyObject.class.cast(object) |
编辑:编译时错误
总体而言,Java仅在运行时执行强制检查。但是,如果编译器可以证明此类强制转换永远不会成功,则编译器可能会发出错误(例如,将一个类强制转换为不是超类型的另一个类,并将最终的类类型强制转换为不在其类型层次结构中的类/接口)。由于
尝试在语言之间翻译构造和概念总是有问题的,并且常常会产生误导。投射也不例外。特别是因为Java是一种动态语言,而C ++却有所不同。
无论您如何执行,所有Java转换都在运行时完成。类型信息在运行时保存。 C ++有点复杂。您可以将C ++中的一个结构强制转换为另一个结构,这只是对代表这些结构的字节的重新解释。 Java不能那样工作。
Java和C ++中的泛型也有很大的不同。不要过度担心自己在Java中如何进行C ++事情。您需要学习如何用Java方式做事。
它与
C样式强制转换运算符的弊端通常不适用于Java。看起来像C样式转换的Java代码与Java中具有引用类型的
通常,将C ++强制转换运算符与Java强制转换进行比较非常困难,因为在Java中,您只能强制转换引用,并且永远不会对对象进行任何转换(只能使用此语法转换原始值)。
C ++和Java是不同的语言。
Java C样式的强制转换运算符比C / C ++版本受限制得多。实际上,如果无法将您拥有的对象强制转换为新类,则Java强制转换就像C ++ dynamic_cast一样,将获得运行时(或代码中有足够的信息作为编译时)异常。因此,在Java中,不使用C类型转换的C ++想法不是一个好主意。
通常,强制转换运算符比Class#cast方法更可取,因为它更简洁,并且可以由编译器进行分析,以消除代码中的公然问题。
Class#cast负责在运行时而不是在编译期间进行类型检查。
当然,有一些Class#cast用例,特别是涉及反射操作时。
自从lambda来到Java以来??,我个人喜欢在例如抽象类型的情况下将Class#cast与collections / stream API结合使用。
1 2 3 4 5 6 7 8 9 | 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); } |
就个人而言,我之前曾用它来构建JSON到POJO转换器。如果使用该函数处理的JSONObject包含一个数组或嵌套的JSONObjects(这意味着此处的数据不是原始类型或
1 2 3 4 5 6 7 8 9 10 | 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是运行时转换,通常与泛型转换一起使用,由于泛型信息将在运行时被删除,并且某些泛型将被视为Object,这导致不引发早期的ClassCastException。
例如serviceLoder在创建对象时使用此技巧,请检查S p = service.cast(c.newInstance());这将引发类强制转换异常
当S P =(S)c.newInstance();不会并且可能显示警告"类型安全性:未经检查的从Object到S的强制转换"。(与Object P =(Object)c.newInstance();相同)
-仅检查铸造对象是否为铸造类的实例,然后将使用铸造运算符通过抑制它来铸造和隐藏警告。
动态转换的Java实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | @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 } |