使用泛型和/或构造函数的对象来返回超类或子类

huangapple go评论73阅读模式
英文:

Using generics and/or objects of Constructor to return superclass OR subclass

问题

摘要:在一个方法中是否可能使用不同的参数获得构造函数,以创建不同子类的对象?


我有一个(非抽象)类,其中有一个扩展它的子类。子类有一个额外的字段。两者都是不可变的:

class Superclass{
    final Type type; // Type是一个枚举;它对于每种类型都有一个final double decayFactor。
    final int amount;

    Superclass(Type type, int amount){//用于初始化final字段的简单构造函数
        ...
    }

    //下面会讨论的decay方法在这里实现
}

class Subclass extends Superclass{
    final boolean additionalProperty;

    Subclass(Type type, int amount, boolean additionalProperty){
        super(type, amount);
        this.additionalProperty = additionalProperty;
    }
}

现在我想要一个方法,在相同的类(SubclassSuperclass)中返回一个带有更新的 amount 的对象。此方法应该通过枚举 Type 定义的某个百分比来减少 amount。这个简单的方法可能看起来像这样(在 Superclass 中实现):

public Superclass decay(){
    return new Superclass(type, amount * type.getDecayFactor());
}

现在我的问题是,如果我有一个 Subclass 并调用这个方法,会在一个方法中返回一个 Superclass。我会丢失存储在 additionalProperty 中的信息。所以我考虑其他解决方法。最简单的方法是在 Subclass 中重写 decay()。然而,我发现我无法强制子类覆盖这个方法。所以,如果以后添加其他子类,如果它们不实现它,同样的信息丢失问题可能会发生。

所以,我尝试通过使用泛型来获取类的构造函数来解决这个问题:

public <T extends Superclass> Superclass decay() throws ...[很多异常]{
    int newAmount = type.getDecayFactor() * amount;
    Constructor<? extends Superclass> constructor = this.getClass().getConstructor(Type.class, int.class);
    return constructor.newInstance(type, amount);
}

我尝试过,但无法在 this.getClass() 的地方使用泛型 T

然而,这也不起作用。我仍然需要重写它,因为 Subclass(Type, int) 不存在。我的想法是,如果我能在类中以某种方式获取所有字段,我可以调用子类或超类的(唯一的)构造函数,并传递它所需的所有值,只修改 amount。我尝试通过使用 this.getClass().getFields() 来获取匹配的构造函数,但它显示:... getConstructor() ... 不适用于参数 Fields[]。(我需要字段的类型?)

我的问题:这种情况是否可能?这是一个优雅或受欢迎的实现吗?还是应该更好地说明子类必须重写 decay(),这在我看来更简单和可读?这里是否需要使用泛型?(我开始逐渐认为不需要……)

附加信息:我最初将 SuperclassSubclass 都设为抽象类,并为每个 Type 枚举编写了一个扩展这些类(SuperclassSubclass)的子类。但是,我将它们包含的信息放入了一个具有 final 字段的枚举中。(本质上可以是表示类似事物的100个类)

decay() 方法的用途是像这样“修改”不可变的 Superclass/Subclass

Superclass someObject = new Superclass/Subclass
...
someObject = someObject.decay();

非常感谢您的回答。

英文:

Summary: Is it possible to get constructors with different arguments to create objects of different subclasses in one method?


I have a (non-abstract) class with a subclass extending it. The subclass has one additional field.
Both are immutable:

class Superclass{
    final Type type; // Type is an enum; It has a final double decayFactor for each type.
    final int amount;

    Superclass(Type type, int amount){//simple constructor initializing final fields
        ...
    }

    //decay method talked about below is implemented here
}

class Subclass extends Superclass{
    final boolean additionalProperty;

    Subclass(Type type, int amount, boolean additionalProperty){
        super(type, amount);
        this.additionalProperty = additionalProperty;
    }
}

Now I would like to have a method which returns an Object of the same class (Subclass or Superclass) with an updated amount.
This method should decrease amount by a certain percentage defined by the enum Type.
This simple method could look like that(implemted in Superclass):

public Superclass decay(){
    return new Superclass(type, amount * type.getDecayFactor())
}

Now my problem is that if I have a Subclass and call this method would return a Superclass in one method. I would lose the information stored in additionalProperty.
So I thought about other ways to solve this. The most simple would be to overwrite decay() in Subclass. However, I found that I cannot force a subclass to override this method. So if later other subclasses are added, the same information loss can happen if they don't implement it.
So I tried to solve this by using generics to get the constructor of the class:

public &lt;T extends Superclass&gt; Superclass decay() throws ...[Many exceptions]{
	int newAmount = type.getDecayFactor() * amount;
    Constructor&lt;? extends Superclass&gt; constructor = this.getClass().getConstructor(Type.class, int.class);
    return constructor.newInstance(type, amount);
}

I tried, but failed to use the generic T instead of this.getClass().

However, this also does not work. I still would have to overwrite it as Subclass(Type, int) does not exist.
My thinking is that if I could somehow get all the fields of the class, I could call the (only) constructor of subclasses OR superclass and pass all the values it needs, only modifing amount. I tried to get a matching Constructor by using this.getClass().getFields(), but it says: ... getConstructor() ... is not applicable for argument Fields[].(I would need the type of fields?)

My question: Is this possible at all? And is this an elegant or favoured implementation? Or should you better state that subclasses have to override decay(), which imho is more simple and readable? Are generics needed here at all?(I slowly begin to think not...)

Additional info: I first had both Superclass and Subclass as abstract and for each Type enum an own subclass extending those(Superclass or Subclass). But then I would have to write very similiar code for each of those classes. So I put the information those contained into an enum with final fields.(Could easily have been 100 classes representing similiar things)

The decay()-method is meant to be used like this to "modify" the immutable Superclass/Subclass:

Superclass someObject = newSuperclass/Subclass
...
someObject = someObject.decay();

Thank you very much for your answers.

答案1

得分: 1

我相信覆盖方法应该能够正常工作。只要它是Superclass的子类型,你就可以更改decay()的返回类型。

你还可以将计算衰减逻辑隔离到一个单独的方法中,以避免冗余代码。

class Superclass {
    final double rate;
    final int amount;

    Superclass(double rate, int amount) {
        this.rate = rate;
        this.amount = amount;
    }

    public Superclass decay() {
        return new Superclass(this.rate, this.calcuateDecay());
    }
    
    protected int calcuateDecay() {
        return (int) (this.amount * this.rate);
    }
}

class Subclass extends Superclass {
    final boolean additionalProperty;

    Subclass(double rate, int amount, boolean additionalProperty) {
        super(rate, amount);
        this.additionalProperty = additionalProperty;
    }
    
    @Override
    public Subclass decay() {
        return new Subclass(this.rate, this.calcuateDecay(), this.additionalProperty);
    }
}

在这里查看它的运行情况:链接

覆盖是可选的。为了进一步加强类型安全性,可以定义泛型接口如下:

interface Decayable<T extends Superclass> {
    T decay();
}

然后,Superclass可以实现Decayable<Superclass>,而Subclass可以实现Decayable<Subclass>。在这里查看它的实际效果:链接

英文:

I believe that overriding the method should work just fine. You can change the return type of decay() long as it is a sub-type of Superclass.

You can also isolate calculating decay logic in a separate method to avoid redudant code.

class Superclass {
    final double rate;
    final int amount;

    Superclass(double rate, int amount) {
        this.rate = rate;
        this.amount = amount;
    }

    public Superclass decay() {
        return new Superclass(this.rate, this.calcuateDecay());
    }
    
    protected int calcuateDecay() {
        return (int) (this.amount * this.rate);
    }
}

class Subclass extends Superclass {
    final boolean additionalProperty;

    Subclass(double rate, int amount, boolean additionalProperty) {
        super(rate, amount);
        this.additionalProperty = additionalProperty;
    }
    
    @Override
    public Subclass decay() {
        return new Subclass(this.rate, this.calcuateDecay(), this.additionalProperty);
    }
}

See it working here.

Overriding here is optional. To further reinforce type safety, generic interface can be defined as follows

interface &lt;T extends Superclass&gt; Decayable&lt;T&gt; {
    T decay();
}

Superclass can then implement Decayable&lt;Superclass&gt; and Subclass can implement Decayable&lt;Subclass&gt;. See it in action here.

huangapple
  • 本文由 发表于 2020年9月18日 22:11:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/63957387.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定