【问题标题】:Returning subclass object from superclass method从超类方法返回子类对象
【发布时间】:2011-04-05 22:19:42
【问题描述】:

我不断回到这个问题的变体:它可能有一个非常简单的解决方案,但我似乎无法弄清楚......

我有一堆 xQuantity 形式的类,例如DistanceQuantity、AreaQuantity 等,它们扩展了一个类 DimensionQuantity。现在您可以添加或减去DistanceQuantity或AreaQuantity等,但不能混合它们,所以我认为我需要在子类中有(短)add,subtract等方法,但我想减少任何逻辑重复到最低限度。但是,我需要返回子类的一个对象,这似乎很难从超类方法中做到。我相信这可以使用反射来完成,但是AFAIK您仍然需要在子类方法的最后进行强制转换,而且我被告知反射可能很昂贵......到目前为止我想出的最好的是:

在 DistanceQuantity(以及其他类似的)中:

public DistanceQuantity() {     
}

public DistanceQuantity add(DistanceQuantity d1) {
    DistanceQuantity dn = new DistanceQuantity(); 
    Object o = super.add(dn, this, d1, DistanceUnit.REF_UNIT);
    return (DistanceQuantity) o;
}

在 DimensionQuantity 中(减去一些不太相关的语句):

public Object add(DimensionQuantity dn, DimensionQuantity d1, DimensionQuantity d2, 
  AbstractUnit au) {
    dn.unit = au;
    dn.scalar = d1.scalar + d2.scalar;
    dn.units = dn.scalar;    
    return dn;   
}

谁能想出更精简的代码——仍然是类型安全的? TIA

【问题讨论】:

    标签: java subclassing


    【解决方案1】:

    你可以像这样使用泛型:

    public abstract class DimensionQuantity<T extends DimensionQuantity>{
        public abstract T add(T parameter);
    }
    

    然后你像这样扩展它:

    public class DistanceQuantity extends DimensionQuantity<DistanceQuantity>{
        public DistanceQuantity add(DistanceQuantity parameter){
            //Whatever
            return null;
        }
    }
    

    对于第一个问题,拥有一个使用其子类之一的超类是一个真的坏主意(也是一种不好的做法)。


    资源:

    关于同一主题:

    【讨论】:

    • 最好将泛型命名为更接近 T_DimensionQuantity 的名称(一旦你有很多泛型就更清楚了)
    • 您忘记了 abstract 关键字。 ;) 我认为仅使用“T”是非常标准的做法。
    • @mangst 谢谢,现在很抽象。 @Egwor,就像@mangst 所说,T 很常见。 T 代表 Type,Collection 中 E 代表 Element,Map 中 K 和 V 代表 Key 和 Value。通常它是泛型含义的第一个字母。
    • @Colin 您是否认为 Guava 的 Ordering 类的设计也很糟糕,因为它直接依赖于子类(nullsLastreverse、...)?只是好奇。
    • @Willi Schönborn 好吧,确切地说,只要子类不是父类的公共接口的一部分,就没有那么糟糕。而且我不应该使用“设计缺陷”,而应该使用“不良做法”。
    【解决方案2】:

    在 Java 6 中,您可以尝试类似的方法。但是使用泛型也是一个好主意:

    public abstract class DimensionQuantity {
    
        protected int quantity;
    
        public DimensionQuantity (int quantity) {
            this.quantity = quantity;
        }
    
        public abstract DimensionQuantity add(DimensionQuantity dq);
    }
    
    public class DistanceQuantity extends DimensionQuantity {
    
        public DistanceQuantity(Quantity quantity) {
            super(quantity);
        }
    
        /* 
         * A sub-class as return type is authorized.
         * If dq is not a DistanceQuantity, a ClassCastException is thrown, but you can change this.
         */
        @Override 
        public DistanceQuantity add(DimensionQuantity dq) throws ClassCastException {
            return new DistanceQuantity(this.quantity + (DistanceQuantity) dq.quantity);
        }
    }
    

    【讨论】:

    • 但是现在您可以将 AreaQuantity 添加到您的 DistanceQuantity。
    • 是的,但这会导致 ClassCastException。所以这个动作是被禁止的,会很快被检测到(但在运行时而不是编译时)。
    • 这真的很危险,如果您将 DistanceQuantity 转换为 DimensionQuantity,您甚至不知道该怎么做而不冒 CCE 的风险。
    【解决方案3】:

    为维度创建一个枚举:

    公共枚举维度 { AREA, DISTANCE,...}

    扔掉你的子类。而是只创建 DimensionQuantity 对象,并强制每个对象都有一个(不可变的)Dimension,在创建时设置。

    在DimensionQuantity中实现加法,首先检查两个加法量的Dimensions是否相同。

    瞧!更少的类,类型安全,没有重复的代码。

    【讨论】:

    • “首先检查要添加的两个量的维度是否相同。” - 对我来说这听起来像是运行时!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-09
    • 1970-01-01
    • 2020-05-03
    • 1970-01-01
    相关资源
    最近更新 更多