views:

585

answers:

4

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.

+9  A: 

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.

krosenvold
Could you give an example?
pn1 dude
May I suggest http://en.wikipedia.org/wiki/Factory_method_pattern
krosenvold
+4  A: 

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.

Dan Vinton
Yes, I think this was the answer I was asking for, but krosenvold gives the obvious and better solution that I knew I was being dumb about. I actually prefer this style over factory methods and it's not difficult to change if File does. However, a factory completely avoids this type of coupling.
Sam Brightman
Call this "stupid day". I think yours actually works just as well.
Sam Brightman
To be honest, I think the factory is a better solution, but krosenvold got there first...
Dan Vinton
+2  A: 

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.

Lars Westergren
Yes, I'm aware of the need to favour delegation but if there is no interface (like with File) to implement via delegation, your new class cannot be used as a direct substitute for the old one (e.g. when passing to other core Java classes).
Sam Brightman
+1  A: 

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;
    }
}
bruno conde