使用子类替换不实现接口的Java类

3
例如,java.io.File只是一个具体类。 我的替代品支持解析Windows快捷方式。 我需要预处理构造函数参数以解析可能的.lnk文件,因为执行规范化/规范化/解析抽象路径的FileSystem对象不可访问。 需要预处理的规则排除了纯子类化 - 无法在调用super(...)之前进行预处理,并且File是不可变的。 因此,我扩展File并使用委托,覆盖所有File的构造函数和方法(在所有构造函数中都调用super(""))。
这很好用,但显然不是理想的 - 如果File发生更改,则不会覆盖任何新方法或构造函数,并且这将公开潜在的空抽象路径名。 我错过了一些明显的东西吗? 看起来应该有更简单/更好的方法。
4个回答

9
在你提出的具体情况中,我认为最好使用一个单独的工厂类来做有关规范化/规范化/解析的决策。然后你可以让文件保持原样,更简单明了。

4

如果你真的需要子类路由,那么你可以通过将清理代码放在类外部或静态块中来欺骗要求在子类构造函数中调用 super() 必须是第一行的要求:

public class MyFile extends File {

    public MyFile(String path) {

        // static blocks aren't ideal, this is just for demo purposes:
        super(cleanPath(path)); 
    }

    private static String cleanPath(String uncleanPath) {...}

}

由krosenvold提出的工厂模式是另一个不错的解决方案。

是的,我认为这就是我所寻求的答案,但Krosenvold提供了一个明显更好的解决方案,而我之前就知道自己有点傻。实际上,我更喜欢这种方式,而不是工厂方法,并且如果File需要更改也不难更改。然而,工厂完全避免了这种类型的耦合。 - Sam Brightman
称呼它为“愚蠢的一天”。我认为你的实际上同样有效。 - Sam Brightman
说实话,我认为工厂是更好的解决方案,但krosenvold先到了那里... - Dan Vinton

2
这个方法虽然有效,但显然不是理想的-如果文件变更,我将无法覆盖任何新方法或构造函数,并且这将暴露底层空抽象路径名。我是否错过了一些明显的东西?
不,您已经发现使用继承的问题-子类与超类及其内部紧密耦合,因此可能很脆弱。这就是为什么Effective Java和其他人说,如果可能的话,应优先使用委托而不是继承。
我认为krosenvold的解决方案听起来很干净。

是的,我知道需要优先使用委托,但如果没有接口(例如File类),无法通过委托实现,那么你的新类就不能直接替换旧类(例如传递给其他核心Java类)。 - Sam Brightman

2

我认为krosenvold的解决方案是正确的。

但是,如果你需要记录创建文件的原始路径,可以实现一个包装类。

public class FileWrapper {

    private File file;
    private String path;

    private FileWrapper(String path) {
        this.path = path;
        file = new File(preProcess(path));
    }

    private String preProcess(String path) {
        // process
        return path;
    }

    public File getFile() {
        return file;
    }

    public String getPath() {
        return path;
    }
}

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