Question

For example, java.io.File is just a concrete class. My replacement for it supports resolving Windows shortcuts. I need to preprocess constructor parameters to resolve possible .lnk files because the FileSystem object that does normalizing/canonicalision/resolving on the abstract paths is not accessible. The need for preprocessing rules out pure subclassing - can't do preprocessing before calling super(...) and File is immutable. So I extend File and use a delegate, overriding all of File's constructors and methods (calling super("") in all constructors).

This works nicely, but is obviously not ideal - if File changes, I will not have overridden any new methods or constructors and this will expose the underlying empty abstract pathname. Am I missing something obvious? It seems like there should be a simpler/better way.

Was it helpful?

Solution

In the specific case you suggest it looks to me like you're better off with a separate factory class that makes the decisions about normalizing/canonicalision/resolving.

Then you can just let File be File. Simpler.

OTHER TIPS

If you really want the subclass route, you can cheat the requirement that a call to super() has to be the first line of a subclass constructor by placing your cleanup code outside of your class, or in a static block:

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) {...}

}

The factory pattern suggested by krosenvold is another good solution.

This works nicely, but is obviously not ideal - if File changes, I will not have overridden any new methods or constructors and this will expose the underlying empty abstract pathname. Am I missing something obvious?

No, you have spotted a problem with using inheritance - that subclasses get tightly coupled to superclasses and their internals, so it can be fragile. That is why Effective Java and others say you should favour delegation before inheritance if possible.

I think krosenvold's solution sounds clean.

It seems to me that krosenvold's solution is the way to go.

But, if you need to keep record of the original path that created the file you can implement a wrapper class.

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;
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top