在Java中的强制向下转型

3
我希望对一个无法进行向下转型的对象进行强制向下转型,并想知道正确的方法是什么。 使用案例是我有一系列规则进行检查,会生成一份失败规则列表。失败规则是规则的子类。但是像这样进行向下转型
FailedRule failedRule = (FailedRule) rule;

将会失败,因为规则对象不是 FailedRule 的实例。

为了解决这个问题,我实例化了一个克隆对象;
FailedRule failedRule = new FailedRule(rule);

我的 FailedRule 类如下所示:

public class FailedRule extends Rule{

/* 
 *force a down cast from Rule to FailedRule through cloning
*/
public FailedRule (Rule upcast){
   super.setRuleCode( upcast.getRuleCode());
   super.setType(upcast.getType());
   ..

有没有更简单的方法来做这件事? 回答自己,设计是有缺陷的,代码应该是:

public class FailedRule{
  private Rule rule;
  ..
  public setRule(Rule rule){
  ..

我想知道你是如何不得不对无法进行下行转换的对象进行“强制下行转换”的。请解释一下你真正想要做什么。 - Nicolas Repiquet
我以为我做到了 :-). 我循环遍历规则列表,并返回未通过的规则列表。检查过程会针对每个规则返回相当数量的信息,说明它为什么以及何时以及如何失败。Mark的答案是正确的,我的设计是错误的,我不能说一个失败的规则“是”规则。它“有一个”规则。我已经编辑了问题,因为我无法在这些评论中放置代码。 - Meindert
6个回答

2
这可能表明你的继承层次结构设计较弱。你正在试图通过继承引入属性的可变性(如果一个“规则”是FailedRule的实例,则说明其“失败”了)。继承并不适合这种情况。
我建议你使用组合(FailedRule作为Rule的来源)或将failed作为Rule实例的布尔属性之一。

好的,所以FailedRule不是规则的子类,因为失败的规则不是规则。 FailedRule类是一个具有规则属性的失败规则; failedRule.setRule(rule); failedRule.setReason(reason)等。 - Meindert
@Meindert:我不确定这是否是一个好的方法,因为我没有太多的上下文。FailedRule提供了哪些额外方面呢?现在规则内部状态发生了什么变化,导致它失败了?如果是我,我会说“如果规则出现在失败规则列表中,则规则失败了。”将规则的概念与其执行结果分离似乎是合理的。就像您不会在汽车本身中存储其上次比赛得分的位置一样;您会有一个引用汽车的RaceRecord - Mark Peters
如果采用组合是正确的方式,我会考虑重新命名FailedRule。它不是一条规则,是吗?它只是一个规则失败的上下文/记录。我会改成RuleFailure或类似的名称。 - Mark Peters
在用户眼里,它是一系列未通过的规则列表...但你说得对,我将未通过的规则存储在名为RuleFail的表中。 - Meindert

2

使用一种将任何规则转换为FailedRule的方法:

public static FailedRule asFailedRule(Rule rule){
    return (rule instanceof FailedRule)
    ? (FailedRule) rule
    : new FailedRule(rule)
}

如果规则已经是一个FailedRule,则将其转换并返回,否则使用它构建一个FailedRule

1

你的解决方案看起来很合理。如果任何规则都有可能失败,将其建模为Rule.isFailed()可能更合适。

编辑:失败听起来非常像一种状态,而不是规则的变体。如果是这种情况,Rule.isFailed()也是首选。如果有些规则真的不会失败,我们可以将其建模为:

           Rule
         /      \
         |       \
    FailableRule  RuleC
     /     |   
 RuleA    RuleB

嗯...一个可失败的规则实际上是一个易犯错误的规则吗?噫...语言学。


иҝҷжҳҜдёҖдёӘеҘҪдё»ж„ҸпјҢеҚідҪҝиҜҘж–№жі•еҸӘеҢ…еҗ« return false; еңЁ Rule дёӯпјҢ并еңЁ FailedRule дёӯиў«иҰҶзӣ–дёә return true;гҖӮ - Erick Robertson
其实我又想到了另一件事。"Failed"听起来非常像是规则的状态,而不是规则的变体。如果是这样的话,“FailedRule”并不是一个很好的面向对象设计。 - Martin Algesten
然而,问题是关于向下转型,而不是评估他的设计选择。 - Erick Robertson
不要一秒钟都同意那里。进入SO的问题中有很大一部分显然是关于人们不理解他们正在处理的技术或问题。如果我看到明显的遗漏或问题思考,我会尽力帮助整个画面,尽管问题与重点无关。这也是为什么用户应该尽可能提供更多的上下文的原因。 - Martin Algesten
提交这个问题的用户可能永远不会回来查看它。来到这里的大多数人都会搜索向下转换。因此,我们应该专注于回答问题,而不是评估那些没有回来澄清的用户的经验。 - Erick Robertson
嘿,我回来了! :-) 我认为给出解决根本问题的答案比简单地回答没有一种轻松的方法来强制向下转换更有帮助。我改变了我的代码,因为我从中得出的结论是失败的规则不是规则类型,而是具有规则属性的失败的规则;我的失败规则还有其他一些属性,例如何时、由谁以及什么导致规则失败。 - Meindert

0

没有更简单的方法了。你做得很正确。

很多时候,我会写一个私有方法,类似这样:

private void copyFromRule(Rule otherRule) {
  this.setRuleCode(otherRule.getRuleCode());
  this.setType(otherRule.getType());
  ...
}

这样,我可以在构造函数中像这样调用它,也可以在需要定义的clone()方法中调用它。

另一点是要知道你是在调用super.setRuleCode还是this.setRuleCode。显然,这两个东西根据是否重新定义setRuleCode而做出不同的事情。


0

你不能像那样将一个类强制转换为子类。这是没有意义的,因为它不会具有子类中的任何方法或变量。你现在的做法是正确的。


0

你现在的做法是正确的。我唯一要补充的评论是将复制代码移到规则本身下面。

public class FailedRule extends Rule{

/* 
 *force a down cast from Rule to FailedRule through cloning
*/
public FailedRule (Rule upcast){
   super(upcast);
   //init FailedRule fields to defaults
}
}

public class Rule {

publiic Rule(Rule ruleToCopy) {
   //or even use the fields themselves. 
   this.setRuleCode( ruleToCopy.getRuleCode());
   this.setType(ruleToCopy.getType());
   ...

谢谢,这是一种更干净的克隆方式。我正在寻找一种“自动克隆”方法,可以将ruleToCopy的所有属性放入“this”中。但我会采用Mark的答案,因为我的设计有缺陷。 - Meindert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接