6

(请不要建议我应该抽象X更多并添加另一种方法。)

在 C++ 中,当我有一个类型的变量x并且如果它也是类型(作为 的子类)X*我想做一些特定的事情,我正在写这个:Y*YX

if(Y* y = dynamic_cast<Y*>(x)) {
    // now do sth with y
}

同样的事情在 Java 中似乎是不可能的(或者是吗?)。

我已经阅读了这段 Java 代码:

if(x instanceof Y) {
    Y y = (Y) x;
    // ...
}

有时,当您没有变量x但它是一个更复杂的表达式时,正是因为这个问题,您需要一个 Java 中的虚拟变量:

X x = something();
if(x instanceof Y) {
    Y y = (Y) x;
    // ...
}
// x not needed here anymore

(常见的something()iterator.next()。而且你看到你也不能真正调用它两次。你真的需要虚拟变量。)

你在这里根本不需要x——你只是拥有它,因为你不能instanceof立即对演员表进行检查。再次将其与相当常见的 C++ 代码进行比较:

if(Y* y = dynamic_cast<Y*>( something() )) {
    // ...
}

正因为如此,我引入了一个castOrNull可以避免虚拟变量的函数x。我现在可以这样写:

Y y = castOrNull( something(), Y.class );
if(y != null) {
    // ...
}

实施castOrNull

public static <T> T castOrNull(Object obj, Class<T> clazz) {
    try {
        return clazz.cast(obj);
    } catch (ClassCastException exc) {
        return null;
    }
}

现在,有人告诉我,以这种方式使用此castOrNull功能是对. 这是为什么?(或者更笼统地说:你会同意并认为这是邪恶的吗?如果是,为什么会这样?或者你认为这是一个有效的(可能是罕见的)用例吗?)


如前所述,我不想讨论这种沮丧的使用是否是一个好主意。但是让我简要说明一下为什么我有时会使用它:

  1. 有时我会遇到这样的情况,即我必须在为非常特定的事物添加另一种新方法(在一种特定情况下仅适用于单个子类)或使用此类instanceof检查之间做出选择。基本上,我可以选择添加功能doSomethingVeryVerySpecificIfIAmY()或进行instanceof检查。而在这种情况下,我觉得后者更干净。

  2. 有时我有一些接口/基类的集合,对于所有类型的条目Y,我想做一些事情,然后将它们从集合中删除。(例如,我有一个树结构的情况,我想删除所有空叶子的孩子。)

4

6 回答 6

8

现在,有人告诉我,以这种方式使用这个 castOrNull 函数是一件坏事。这是为什么?

我能想到几个原因:

  • 这是做一些非常简单的事情的一种晦涩难懂的方法。晦涩难懂的代码难以阅读,难以维护,是潜在的错误来源(当有人不理解时),因此是邪恶的。

  • castOrNullJIT 编译器最有可能无法优化该方法工作的晦涩和棘手的方式。您最终会得到至少 3 个额外的方法调用,以及大量额外的代码来进行类型检查和反射性转换。不必要地使用反射是邪恶的。

(相比之下,简单的方法(instanceof后跟一个类转换)使用特定的字节码进行 instanceof 和类转换。字节码序列几乎可以肯定会被优化,这样就不会超过一次空检查,也不会超过一次测试对象在本机代码中的类型。这是一种常见的模式,JIT 编译器应该很容易检测和优化。)

当然,“邪恶”只是另一种说法,你真的不应该这样做。

您添加的两个示例都没有使用castOrNull必要或可取的方法。IMO,从可读性和性能的角度来看,“简单的方式”更好。

于 2010-10-16T15:06:30.047 回答
7

从 Java 14 开始,您应该能够同时执行instanceof和转换。请参阅https://openjdk.java.net/jeps/305

代码示例:

if (obj instanceof String s) {
    // can use s here
} else {
    // can't use s here
}

instanceof如果计算结果为真,则定义上述示例中的变量 s 。变量的范围取决于上下文。有关更多示例,请参见上面的链接。

于 2019-12-17T01:19:05.097 回答
5

在大多数编写/设计良好的 Java 代码中,从不使用 instanceof 和 casts。随着泛型的添加,许多强制类型转换(以及 instanceof)的情况都不再需要了。他们确实,有时仍然会发生。

castOrNull 方法是邪恶的,因为您使 Java 代码看起来“不自然”。从一种语言转换到另一种语言时最大的问题是采用新语言的约定。临时变量在 Java 中就很好。实际上,您所做的所有方法实际上都是隐藏临时变量。

如果你发现你正在编写大量的演员表,你应该检查你的代码并找出原因并寻找删除它们的方法。例如,在您提到添加“getNumberOfChildren”方法的情况下,您可以检查节点是否为空,从而能够在不强制转换的情况下修剪它(这是一种猜测,在这种情况下它可能对您不起作用)。

一般来说,强制转换在 Java 中是“邪恶的”,因为通常不需要它们。您的方法更“邪恶”,因为它不是以大多数人期望的 Java 编写方式编写的。

话虽这么说,如果你想这样做,那就去做吧。在 Java 中,它实际上并不是“邪恶”的,只是不是“正确”的方式。

于 2010-10-16T15:39:28.673 回答
4

恕我直言,你castOrNull不是邪恶的,只是毫无意义。你似乎痴迷于摆脱一个临时变量和一行代码,而对我来说更大的问题是为什么你的代码需要这么多的向下转换?在 OO 中,这几乎总是次优设计的症状。我更愿意解决根本原因而不是治疗症状。

于 2010-10-16T14:21:13.743 回答
2

我不知道为什么那个人说它是邪恶的。但是,他们推理的一种可能性是,您事后发现了异常,而不是在施放之前进行检查。这是一种方法。

public static <T> T castOrNull(Object obj, Class<T> clazz) {
    if ( clazz.isAssignableFrom(obj.getClass()) ) {
        return clazz.cast(obj);
    } else {
        return null;
    }
}
于 2010-10-16T14:15:46.380 回答
0

Java 异常很慢。如果您试图通过避免双重转换来优化您的性能,那么您使用异常代替逻辑是在自取其辱。永远不要依赖于为您可以合理检查和纠正的事情(正是您正在做的事情)捕获异常。

Java 异常有多慢?

于 2015-07-16T14:59:51.387 回答