创建不可变派生类的修改实例

时间:2017-10-25 01:16:18

标签: java generics inheritance immutability

假设我有一个抽象基类和许多不可变的派生类:

public abstract class Communication{
    private final String message;
    private final Medium medium;

    // getters, other methods, etc.

    public Communication(String message, Medium medium){
        this.message = message;
        this.medium = medium;
    }
}

public class HateMail extends Communication{
    public Hatemail(String message){
        super(message, Medium.Written);
    }
}

public class CasualGreeting extends Communication{
    public CasualGreeting(String message){
        super(message, Medium.Speech);
    }
}

我想要做的是"修改"与Communication类似的String,类似于:

HateMail hateMail = new HateMail("I'm saying some really mean things.");
hateMail = hateMail.censor();

从censor()返回的新对象是派生类型而不是基类型。

我曾想过实现这个的一些方法:

1.使用Class.newInstance()

public abstract class Communication{
    private final String message;
    private final Medium medium;

    // getters, other methods, etc.

    public Communication(String message, Medium medium){
        this.message = message;
        this.medium = medium;
    }

    public <T extends Communication> T censor(){
        String censored = // Censorship logic

        Communication newComm = this.getClass().newInstance();
        newComm.message = censored;
        newComm.medium = this.medium;

        return (T) newComm;
    }
}

但这种方式非常糟糕,我必须为所有派生类实现无参数构造函数。

2.使用Class.getConstructors()。但这并不比以前的解决方案好得多。

3.在Communication中有一个抽象方法,在每个派生类中重写,返回该类的正确转换实例。但这感觉它确实违反了DRY,因为这些方法只会在他们返回的课程中有所不同。

我觉得必须有一种更简单的方法来使用泛型,我只是不知道它的语法。

2 个答案:

答案 0 :(得分:1)

一般来说,你想要做的是一个坏主意,并且可能有更好的设计可以避免这个问题。

让我解释一下。

如果censor是适用于每个Communication的方法,那么它应该是该类中的方法。但是,现在这意味着它应该适用于Communication的任何变量,因此应该返回Communication。例如,这将允许子类决定审查通信返回不同的子类。你可能会认为这种情况永远不会发生,直到它发生的时候!

但是,如果censor仅适用于Communication的某些子类,则它应该是每个子类中的一个适当命名的方法,并返回该类的对象。

例如,如果我们假设所有通信都能够通过默认的无审查进行审查,那么您可能会遇到以下情况:

abstract class Communication {
    private final String message;
    private final Medium medium;

    protected Communication(String message, Medium medium) {
        this.message = message;
        this.medium = medium;
    }

    public Communication censor() {
        return this;
    }
}

class HateMail extends Communication {
    public HateMail(String message) {
        super(message, Medium.EMAIL);
    }

    @Override
    public Communication censor() {
        return new HateMail(toCensoredMessage(message));
    }
}

class Greeting extends Communication {
    public Greeting(String message) {
        super(message, Medium.SPEECH);
    }
}

请注意,在this方法中默认返回censor是完全安全的,因为您的类是不可变的。 (请注意,您在问题中发布的代码无法编译,因为您在调用newInstance后将值分配给最终变量。)

我怀疑你的问题确实隐藏了一个完全不同的问题:一旦通信被审查(因此,现在是Communication),你如何调用你知道特定于原始子类的方法。我的建议是考虑Visitor pattern。这使您可以优雅地处理不知道新类是什么而不诉诸投射或反射。

答案 1 :(得分:-1)

你不需要泛型。允许子类返回比超类更复杂的返回类型。

你可以这样写 - 共享审查逻辑将在受保护的方法censored()中(显然,审查逻辑可以为所有通信类一般定义 - 如果不是这样, censored()方法只应在子类中定义:

public abstract class Communication{
    private final String message;
    private final Medium medium;

    // getters, other methods, etc.

    public Communication(String message, Medium medium){
        this.message = message;
        this.medium = medium;
    }

    protected String censored() {
        return // Censorship logic
    }

    public abstract Communication censor();
}

public class HateMail extends Communication{
    public HateMail(String message){
        super(message, Medium.Written);
    }

    public HateMail censor() {
        return new HateMail(censored());
    }
}