java cast_泛型 - Java Class.cast()与cast op

泛型 - Java Class.cast()与cast op

在我的C ++时代被教授关于C风格演员的邪恶之后,我很高兴首先发现Java 5 java.lang.Class已经获得了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 );

}

}

}

所以,这些是我的问题。

Class.cast()应该被放逐到仿制药土地吗? 它有很多合法的用途。

当使用Class.cast()并且编译时可以确定非法条件时,编译器是否应该生成编译错误?

Java是否应该提供类似于C ++的转换操作符作为语言结构?

7个解决方案

102 votes

我曾经只使用过Class.cast(Object)以避免在“generics land"”中发出警告。 我经常看到方法做这样的事情:

@SuppressWarnings("unchecked")

T doSomething() {

Object o;

// snip

return (T) o;

}

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

T doSomething(Class cls) {

Object o;

// snip

return cls.cast(o);

}

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

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

sfussenegger answered 2019-08-10T16:28:57Z

17 votes

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

在任何情况下,主要在通过反射检索Bar令牌时应使用Foo。 写作比较惯用

MyObject myObject = (MyObject) object

而不是

MyObject myObject = MyObject.class.cast(object)

编辑:编译时的错误

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

notnoop answered 2019-08-10T16:29:53Z

16 votes

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

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

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

cletus answered 2019-08-10T16:30:35Z

10 votes

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

它与dynamic_cast<>()不相似,因为它不允许你在运行时打破类型系统,而不是正常的转换(即你可以&#34;打破&#34;泛型类型参数,但可以&#39; t& #34;打破&#34;&#34;真实&#34;类型)。

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

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

Joachim Sauer answered 2019-08-10T16:31:26Z

3 votes

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

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

Mark answered 2019-08-10T16:32:06Z

0 votes

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

例如,serviceLoder在创建对象时使用此技巧,检查S p = service.cast(c.newInstance()); 这将抛出一个类强制转换异常当S P =(S)c.newInstance(); 赢了,并且可能会显示警告&#39;类型安全:未选中从对象转换为S&#39;。(与对象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

}

Amjad Abdul-Ghani answered 2019-08-10T16:32:57Z

0 votes

就个人而言,我之前使用过它来构建一个JSON到POJO转换器。 在使用函数处理的JSONObject包含数组或嵌套的JSONObjects(暗示此处的数据不是基本类型或class.cast())的情况下,我尝试以这种方式使用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()之一,至少你现在有另一个例子。

Wep0n answered 2019-08-10T16:33:33Z

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值