2

When I try to implement my own ImmutableList (actually a wrapper that delegates to the underlying list) I get the following compiler error:

ImmutableListWrapper is not abstract and does not override abstract method isPartialView() in com.google.common.collect.ImmutableCollection

But in fact, it seems to be impossible to override isPartialView() because it is package protected and I'd like to declare the wrapper in my own package.

Why don't I simply extend ImmutableCollection? Because I want ImmutableList.copyOf() to return my instance without making a defensive copy.

The only approach I can think of is declaring a subclass in guava's package which changes isPartialView() from package-protected to public, and then having my wrapper extend that. Is there a cleaner way?

What I am trying to do

I am attempting to fix https://github.com/google/guava/issues/2029 by creating a wrapper that would delegate to the underlying ImmutableList for all methods except spliterator(), which would it override.

I am working under the assumption that users may define variables of type ImmutableList and expect the the wrapper to be a drop-in replacement (i.e. it isn't enough to implement List, they are expecting an ImmutableList).

Gili
  • 86,244
  • 97
  • 390
  • 689
  • `ImmutableList.copyOf` is static and cannot be overriden. You have a problem, you're thinking about and you have another problem figuring out the solution of it but it's the second one you want to fix first. This is an XY problem. – Olivier Grégoire Jul 28 '15 at 08:18
  • 4
    Guava deliberately forbids you from doing this. Please find a better solution that doesn't involve breaking Guava's abstractions - that doesn't involve trying to subclass ImmutableList at all. – Louis Wasserman Jul 28 '15 at 15:33
  • 1
    @LouisWasserman I've updated the question to explain what I am trying to do. Can I achieve the same goal without subclassing ImmutableList? – Gili Jul 28 '15 at 15:55
  • 1
    Probably not even by subclassing ImmutableList. A forwarding wrapper is probably the best you can do, or the workaround discussed in that bug thread. – Louis Wasserman Jul 28 '15 at 16:01
  • Note that Guava is now Java-8 compatible, so you should be good to go. – dimo414 Mar 20 '17 at 21:47

3 Answers3

3

If you want your own immutable list but don't want to implement it, just use a ForwardingList. Also, to actually make a copy, use Iterator as parameter for the copyOf. Here's a solution that should fulfill all your requirements described in the question and your answer.

public final class MyVeryOwnImmutableList<T> extends ForwardingList<T> {
  public static <T> MyVeryOwnImmutableList<T> copyOf(List<T> list) {
    // Iterator forces a real copy. List or Iterable doesn't.
    return new MyVeryOwnImmutableList<T>(list.iterator());
  }

  private final ImmutableList<T> delegate;

  private MyVeryOwnImmutableList(Iterator<T> it) {
    this.delegate = ImmutableList.copyOf(it);
  }

  @Override
  protected List<T> delegate()
  {
    return delegate;
  }
}
Sean Bright
  • 118,630
  • 17
  • 138
  • 146
Olivier Grégoire
  • 33,839
  • 23
  • 96
  • 137
2

If you want different behavior than ImmutableList.copyOf() provides, simply define a different method, e.g.

public class MyList {
  public static List<E> copyOf(Iterable<E> iter) {
    if (iter instanceof MyList) {
      return (List<E>)iter;
    return ImmutableList.copyOf(iter);
  }
}

Guava's immutable classes provide a number of guarantees and make a number of assumptions about how their implementations work. These would be violated if other authors could implement their own classes that extend Guava's immutable types. Even if you correctly implemented your class to work with these guarantees and assumptions, there's nothing stopping these implementation details from changing in a future release, at which point your code could break in strange or undetectable ways.

Please do not attempt to implement anything in Guava's Imutable* heirarchy; you're only shooting yourself in the foot.

If you have a legitimate use case, file a feature request and describe what you need, maybe it'll get incorporated. Otherwise, just write your wrappers in a different package and provide your own methods and guarantees. There's nothing forcing you, for instance, to use ImmutableList.copyOf(). If you need different behavior, just write your own method.

dimo414
  • 47,227
  • 18
  • 148
  • 244
  • I've updated the question to explain what I am trying to do. Can I achieve the same goal without subclassing ImmutableList? – Gili Jul 28 '15 at 15:58
  • I would either wait until Guava supports Java 8, or accept the O(n) copy cost. They're actively working on it, as mentioned in the bug, let them get to it. – dimo414 Jul 28 '15 at 16:44
-2

Upon digging further, it looks like this limitation is by design:

Quoting http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/ImmutableList.html:

Note: Although this class is not final, it cannot be subclassed as it has no public or protected constructors. Thus, instances of this type are guaranteed to be immutable.

So it seems I need to create my wrapper in the guava package.

Gili
  • 86,244
  • 97
  • 390
  • 689
  • 6
    "*I need to create my wrapper in the guava package.*" please don't do this. Not only does it abuse the package hierarchy (you don't own `com.google`) but it will violate the assumptions anyone using your code makes. Guava's immutable classes cannot be extended on purpose. – dimo414 Jul 28 '15 at 04:25
  • 1
    @dimo414 I've updated the question to explain what I am trying to do. Can I achieve the same goal without subclassing ImmutableList? – Gili Jul 28 '15 at 15:58