views:

397

answers:

3

If an object takes a reference to another object, I know it can be a good idea to make a copy of the passed-in object to preserve encapsulation. But, what if all the object knows about the object passed in is that it implements an interface?

For example, if I have an object that takes an implementation of FilenameFilter in its constructor, how can I make a defensive copy of it when all I know about it is that it implements FilenameFilter? Would I have to resort to using reflection?

+4  A: 

Your best bet when dealing with this kind of problem is to make as many of your objects as possible immutable, meaning they can't be modified after creation. If you need to modify them you make a new one leaving the original untouched.

This is also better for multithreading as well.

Mutable objects aren't considered "best practice" as a general rule. You can't completely eliminate them but, as Josh Bloch says, favour immutability.

That being said, there's no real way of making a deep copy (which is what you'd need) in any kind of guaranteed way. If you're simply passed in an interface, then your only options are really reflection of some form or making a copy to another object that implements the same interface (if thats possible).

cletus
Thanks for the reply. I agree it's best to make objects immutable. But, if an interface is passed to me and I don't know its implementation, it's out of my control.Since a deep copy with reflection would probably be impossible, it would be better to advise users in the docs to pass a FilenameFilter that is immutable.I've read some of "Java Concurrency in Practice". That's what got me thinking about this. It's a great book, but some of the things they advocate, like making deep copies of objects passed in, seems to fly in the face of coding to interfaces.
+2  A: 

First of all, it's not always a good idea to make a defensive copy. You only want to do that with mutable classes that cannot enforce the contract that your business logic requires (such as collections).

Yours is a perfect example. FilenameFilter is immutable--nothing can change it through that interface. Feel free to pass it around at will. The only potential problem is threading, and that's pretty rarely a problem.

The more general solution (For classes that are actually an issue like collections) would be to wrap the collection in a class rather than copy it. If you wrap it in a business logic class, you can restrict access until you are sure it won't break your business logic rules, then feel free to pass it everywhere.

You'll notice that the JDK pretty much never passes collections. This is on purpose, and the exact reason for the "Copying" pattern you are considering. Usually the JDK will pass an array, and most collections have a fairly easy way to generate a copy of their contents to pass on.

Copying an object is actually extremely rare--collections and arrays are the primary ones to be concerned with. Most other objects can defend themselves pretty well if they are even half-decently designed.

Bill K
Thanks for the reply. I agree with most of what you said.The only thing I take issue with is that a FilenameFilter is immutable. I think it depends on the implementation. Let's say the implementation has a boolean called inclusive with a getter and setter. If the developer passes the FilenameFilter to my object with the inclusive value set to true and then, for some reason, changes it to false, he will get unexpected results if he wanted my copy to remain inclusive.With that said, the idea of a deep copy using reflection seems so error prone it would be more risky than trusting the client.
A: 

I'd start by thinking carefully about whether the defensive copy is really necessary. If this is not a security related issue, then it sounds like the defensive copy is not strictly necessary in this case.

Then assuming that the copy is strictly necessary, I'd define an subinterface of FileFilter that defines a copy method with appropriate semantics. Use that interface instead of FileFilter in the constructor you mentioned, pass FileFilters that implement the interface, and arrange for the afore-mentioned constructor to call the copy method at the appropriate time.

(Alternatively, you might be able to use Object.clone() depending on your app's package structure.)

Stephen C
I think you and the other two responders have pretty much convinced me that a defensive copy is probably not necessary. I like your idea of creating a subinterface that defines a copy method, though.