Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Here is a recent discussion on Guava mailing list - my answer touches upon this, rather fundamental issue.</p> <p><a href="http://groups.google.com/group/guava-discuss/browse_thread/thread/2d422600e7f87367/1e6c6a7b41c87aac" rel="nofollow noreferrer">http://groups.google.com/group/guava-discuss/browse_thread/thread/2d422600e7f87367/1e6c6a7b41c87aac</a></p> <p>The gist of it is this: <strong>Don't use marker interfaces when you expect your objects to be wrapped</strong>. (Well, that's pretty general - how <em>do</em> you know that your object isn't going to be wrapped by a client?)</p> <p>For example, an <code>ArrayList</code>. It implements <code>RandomAccess</code>, obviously. Then you decide to create a wrapper for <code>List</code> objects. Oops! Now when you wrap, you have to check the wrapped object, and if it is RandomAccess, the wrapper you create should <strong>also</strong> implement RandomAccess!</p> <p>This works "fine"...if you only have a single marker interface! But what if the wrapped object can be Serializable? What if it is, say, "Immutable" (assuming you have a type to denote that)? Or synchronous? (With the same assumption). </p> <p>As I also note in my answer to the mailing list, this design deficiency also manifest itself in the good old <code>java.io</code> package. Say you have a method accepting an <code>InputStream</code>. Will you read directly from it? What if it is a costly stream, and nobody cared to wrap it in a <code>BufferedInputStream</code> for you? Oh, that's easy! You just check <code>stream instanceof BufferedInputStream</code>, and if not, you wrap it yourself! But no. The stream might have buffering somewhere down the chain, but you may get a wrapper of it, <strong>that is not an instance of BufferedInputStream</strong>. Thus, the information that "this stream is buffered" is lost (and you have to pessimistically waste memory to buffer it again, perhaps). </p> <p>If you want to do things <strong>properly</strong>, just model the capabilities as objects. Consider:</p> <pre><code>interface YourType { Set&lt;Capability&gt; myCapabilities(); } enum Capability { SERIALIAZABLE, SYNCHRONOUS, IMMUTABLE, BUFFERED //whatever - hey, this is just an example, //don't throw everything in of course! } </code></pre> <p><strong>Edit:</strong> It should be noted that I use an enum just for convenience. There could by an interface <code>Capability</code> and an open-ended set of objects implementing it (perhaps multiple enums).</p> <p>So when you wrap an object of these, you get a Set of capabilities, and you can easily decide <em>which capabilities to retain, which to remove, which to add</em>.</p> <p>This <strong>does</strong>, obviously, have its shortcomings, so it is to be used only in cases where you really feel the pain of wrappers hiding capabilities expressed as marker interfaces. For example, say you write a piece of code that takes a List, but it has to be RandomAccess <strong>AND</strong> Serializable. With the usual approach, this is easy to express:</p> <pre><code>&lt;T extends List&lt;Integer&gt; &amp; RandomAccess &amp; Serializable&gt; void method(T list) { ... } </code></pre> <p>But in the approach I describe, all you can do is:</p> <pre><code>void method(YourType object) { Preconditions.checkArgument(object.getCapabilities().contains(SERIALIZABLE)); Preconditions.checkArgument(object.getCapabilities().contains(RANDOM_ACCESS)); ... } </code></pre> <p>I really wish there were a more satisfying approach than either, but from the outlook, it seems not doable (without, at least, causing a combinatorial type explosion).</p> <p><strong>Edit:</strong> Another shortcoming is that, without an explicit <em>type</em> per capability, we don't have the natural place to put methods that express what this capability offers. This is not too important in this discussion since we talk about <em>marker</em> interfaces, i.e. capabilities that are not expressed through additional methods, but I mention it for completeness.</p> <p>PS: by the way, if you skim through Guava's collections code, you can really feel the pain that this problem is causing. Yes, some good people are trying to hide it behind nice abstractions, but the underlying issue is painful nonetheless.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload