Here's a recent discussion on the Guava mailing list - my answer addresses this rather fundamental issue.
http://groups.google.com/group/guava-discuss/browse_thread/thread/2d422600e7f87367/1e6c6a7b41c87aac
The bottom line is: Do not use marker interfaces when you expect your objects to be wrapped . (Well, this is pretty general - how do you know that your object will not be wrapped by the client?)
For example, ArrayList . It implements RandomAccess , obviously. Then you decided to create a wrapper for the List objects. Unfortunately! Now when you wrap, you should check the wrapped object, and if it is RandomAccess, the created shell should also implement RandomAccess!
This works fine ... if you only have one marker interface! But what if a wrapped object can be Serializable? What if it's, say, “Immutable” (if you have a type to denote this)? Or synchronously? (With the same assumption).
As I also point out in my response to the mailing list, this design java.io also evident in the old java.io package. Say you have a method that accepts an InputStream . Will you read right from it? What if it is an expensive stream and no one cared to wrap it in a BufferedInputStream for you? Oh it's easy! You just check the stream instanceof BufferedInputStream , and if not, you wrap it yourself! But no. A stream can buffer somewhere down the chain, but you can get its wrapper , which is not an instance of BufferedInputStream . Thus, the information that “this stream is buffered” is lost (and you must pessimistically destroy the memory in order to reload it again).
If you want to do something right , just model features as objects. Consider:
interface YourType { Set<Capability> myCapabilities(); } enum Capability { SERIALIAZABLE, SYNCHRONOUS, IMMUTABLE, BUFFERED
Edit: It should be noted that I use the enumeration for convenience only. There may be a Capability interface and an open set of objects that implement it (possibly several enumerations).
So, when you wrap an object from them, you get a set of features, and you can easily decide which features to keep that you need to remove to add.
This one , obviously, has its drawbacks, so it should be used only in cases where you really feel pain in the shells that hide the possibilities expressed as marker interfaces. For example, let's say you write a piece of code that takes a list, but it must be RandomAccess AND Serializable. With the usual approach, this is easy to express:
<T extends List<Integer> & RandomAccess & Serializable> void method(T list) { ... }
But in the approach described above, all you can do is:
void method(YourType object) { Preconditions.checkArgument(object.getCapabilities().contains(SERIALIZABLE)); Preconditions.checkArgument(object.getCapabilities().contains(RANDOM_ACCESS)); ... }
I really wish there was a more satisfactory approach than any of them, but from the perspective it seems not feasible (without, at least, causing the explosion of a combinatorial type).
Edit: Another drawback is that without an explicit type for each opportunity, we do not have a natural place to place methods that express what this opportunity offers. This is not very important in this discussion, since we are talking about marker interfaces, that is, capabilities that are not expressed using additional methods, but I mentioned this for completeness.
PS: By the way, if you are looking at the code of the Guava collection, you really can feel the pain that this problem causes. Yes, some good people try to hide it behind beautiful abstractions, but the main problem is still painful.