简体   繁体   English

在构造函数中调用可覆盖的方法很糟糕。 有例外吗?

[英]Calling an overridable method in constructor is bad. Are there exceptions?

I was wondering if there are cases where calling public or, in this case and particularly, protected methods in the constructor of an abstract class would be ok, or at least forgivable, given an adequate documentation of the intentions of the method. 我想知道是否存在调用public的情况,或者在这种情况下,特别是抽象类的构造函数中受保护的方法可以正常,或者至少可以原谅,给出足够的方法意图的文档。

My actual question concerns an abstract class IdentifiedConnection that looks like this: 我的实际问题涉及一个抽象类IdentifiedConnection ,如下所示:

public abstract class IdentifiedConnection extends Connection {

    private UUID uuid;

    public IdentifiedConnection(String serverAddress, int port) throws IOException {
        super(serverAddress, port);
        this.uuid = this.aquireIdentifier();
    }

    public UUID getIdentifier() {
        return this.uuid;
    }

    protected abstract UUID aquireIdentifier() throws IOException;

}

My intention is to get a UUID identifier from the server. 我的目的是从服务器获取UUID标识符。 If the server responds with a valid UUID, we set that UUID in the field of this class (named uuid ). 如果服务器以有效的UUID响应,我们在该类的字段中设置该UUID(名为uuid )。 If the server responds with a non-UUID message, we assume that the server is unreachable for some reason and throw an IOException . 如果服务器以非UUID消息响应,我们假设服务器由于某种原因无法访问并抛出IOException

Generally I understand that calling an overridable method from the constructor is bad, since it leaves the class prone to various more or less hard-to-detect bugs. 通常我理解从构造函数调用一个可覆盖的方法是不好的,因为它使类容易出现各种或多或少难以检测的错误。 However, in this case I feel like doing this isn't actually too bad (as long as I javadoc the hell out of it). 然而,在这种情况下,我觉得这样做实际上并不是太糟糕(只要我javadoc地狱了)。

What's your thoughts? 你有什么想法? Is this still a really bad idea? 这仍然是一个非常糟糕的主意吗? What alternative way of doing this do you suggest if you think this is bad? 如果您认为这样做不好,您建议采用哪种替代方法?


I have left the code for the Connection class out of this question as I think it's irrelevant. 我已经从这个问题中删除了Connection类的代码,因为我认为这是无关紧要的。

I would recommend you to use a factory method in this case. 在这种情况下,我建议你使用工厂方法。

Write a static method called something like 写一个名为的静态方法

public static IdentifiedConnection openConnection(String serverAddress, int port)
        throws IOException {
    ...
}

This allows for better control of the creation and avoids the potential problems with leaking references to uninitialized objects. 这样可以更好地控制创建,并避免泄漏对未初始化对象的引用的潜在问题。


Another approach would be to take a Supplier<UUID> as argument to the constructor, as follows: 另一种方法是将Supplier<UUID>作为构造函数的参数,如下所示:

abstract class IdentifiedConnection extends Connection {

    private UUID uuid;

    public IdentifiedConnection(String serverAddress,
                                int port,
                                Supplier<UUID> uuidSupplier) throws IOException {
        super(serverAddress, port);
        this.uuid = uuidSupplier.get();
    }
}

and in a subclass use it as 在子类中使用它作为

class SomeConnection extends IdentifiedConnection {

    public SomeConnection(String serverAddress, int port) throws IOException {
        super(serverAddress, port, SomeConnection::createUUID);
    }

    public static UUID createUUID() {
        return ...;
    }

}

Having a base-class constructor invoke virtual or abstract methods is a perfectly fine and reasonable thing to do if the documentation for such methods expressly states that they will be called from the base-class constructor . 如果这些方法的文档明确声明它们将从基类构造函数中调用,那么使用基类构造函数调用虚拟或抽象方法是完全正确和合理的。 Such calls can be very useful in cases where eg a subclass needs to affect the value to be stored in a final base-class field, or where establishment of the base-class invariants would entail exposing the newly-constructed object to outside code. 在例如子类需要影响要存储在final基类字段中的值,或者基类不变量的建立需要将新构造的对象暴露给外部代码的情况下,这样的调用可能非常有用。 Unfortunately, there is no nice way for a derived-class method which runs before the derived-class constructor to get access to any of the derived class constructor parameters. 不幸的是,在派生类构造函数之前运行的派生类方法没有很好的方法来访问任何派生类构造函数参数。 It would be possible for each layer of a class to define a constructor-parameter class, each of which derives from the next lower layer's constructor-parameter type, and then have the base class pass that object through to a virtual method which is called in the base constructor, but that's a bit hokey. 类的每一层都可以定义一个构造函数 - 参数类,每个类都派生自下一个较低层的构造函数 - 参数类型,然后让基类将该对象传递给一个被调用的虚方法。基础构造函数,但这有点儿。 Still, having a virtual init(ConstructorParams) method may make it possible for derived classes to initialize themselves before the base class constructor returns--something which is otherwise very difficult. 尽管如此,使用虚拟init(ConstructorParams)方法可以使派生类在基类构造函数返回之前初始化自身 - 这是非常困难的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM