tmn
tmn

Reputation: 11559

ImmutableList vs List- what should I cast it as?

I know its commonly accepted to cast all List implementations down to List. Whether it is a variable, method return, or a method parameter using an ArrayList, CopyOnWriteArrayList, etc.

List<Market> mkts = new ArrayList<>();

When I'm using a Guava ImmutableList, I have the sense it can arguably be an exception to this rule (especially if I'm building in-house, complicated business applications and not a public API). Because if I cast it down to list, the deprecated mutator methods will no longer be flagged as deprecated. Also, it no longer is identified as an immutable object which is a very important part of its functionality and identity.

List<Market> mkts = ImmutableList.of(mkt1,mkt2,mkt3);

Therefore it makes sense to pass it around as an ImmutableList right? I could even argue that its a good policy for an internal API to only accept ImmutableList, so mutability and multithreading on the client side won't wreck anything inside the library.

ImmutableList<Market> mkts = ImmutableList.of(mkt1,mkt2,mkt3);

I know there is a risk of ImmutableList itself becoming deprecated, and the day Oracle decides to create its own ImmutableList will require a lot of refactoring. But is it arguable the pros of maintaining an ImmutableList cast can outweigh the cons?

Upvotes: 7

Views: 13020

Answers (5)

Fritz Duchardt
Fritz Duchardt

Reputation: 11900

Keep using List rather than ImmutableList! There is no problem with that and no reason for your API to start using ImmutableLists explicitly for several reasons:

  1. ImmutableList is Guava only and unlikely to become standard Java at any point. Don't tie your code and coding habits to a third party library (even if it is a cool one like Guava).
  2. Using immutable objects is good practice in Java and of particular importance when developing an API (see Effective Java Item 15 - minimize mutability). It is a general concept that can be taken for granted and does not need to be conveyed in the name of interfaces. Equally, you would not consider calling a User class that is designed for inheritance UserThatCanBeSubclassed.
  3. In the name of stability your API should NEVER start modifying a List that was passed into it and ALWAYS make a defensive copy when passing a List to a client. Introducing ImmutableList here would lure you and the clients of your API into a false sense of security and entice them to violate that rule.

Upvotes: 2

Stephen C
Stephen C

Reputation: 718926

I agree with your rationale. If you are using the Guava collection library and your lists are immutable then passing them as ImmutableList is a good idea.

However:

I know there is a risk of ImmutableList itself becoming deprecated, and the day Oracle decides to create its own ImmutableList will require a lot of refactoring.

The first scenario seems unlikely, but it is a risk you take whenever you use any 3rd-party library. But the flipside is that you could chose to not upgrade your application's Guava version if they (Google) gratuitously deprecated a class or method that you relied on.

UPDATE

Louis Wasserman (who works for Google) said in a comment:

"Guava provides pretty strong compatibility guarantees for non-@Beta APIs."

So we can discount the possibility of gratuitous API changes.


The second scenario is even more unlikely (IMO). And you can be sure that if Oracle did add an immutable list class or interface, that would not require you to refactor. Oracle tries really hard to avoid breaking existing code when they enhance the standard APIs.

But having said that, it is really up to you to weigh up the pros and cons ... and how you would deal with the cons should the eventuate.

Upvotes: 8

maaartinus
maaartinus

Reputation: 46432

Unfortunately, there's no corresponding interface in Java (and most probably never will be). So my take is to pretend that ImmutableList is an interface. :D But seriously, it add important information which shouldn't get lost.

The ancient rule it all comes from actually states something like "program against interfaces". IIRC at the time the rules was created, there was no Java around and "interface" means programming interface, i.e., the contract, not java interface.

A method like

void strange(ArrayList list) {...}

is obviously strange, as there's no reason not to use List. A signature containing ImmutableList has a good reason.


I know there is a risk of ImmutableList itself becoming deprecated, and the day Oracle decides to create its own ImmutableList will require a lot of refactoring.

You mean Java 18? Let's see, but Guava's ImmutableList is pretty good and there's not much point in designing such a class differently. So you can hope that most changes will be in your imports only. And by 2050 there'll be worse problems than this.

Upvotes: 2

Michal Kordas
Michal Kordas

Reputation: 10925

I would rather stay with just List for method parameter. There is no much benefit to enforce the caller to pass ImmutableList - it's your own method and you won't mutate list anyway, but you'd have method more reusable and generic.

As a return type, I would go with ImmutableList to let method users know that this list cannot be modified.

Upvotes: 0

Olivier Croisier
Olivier Croisier

Reputation: 6149

I understand your dilemma.

Personnaly, I would advise to keep using List as the reference type (to be future-proof and benefit from polymorphism), and use an @Immutable annotation to convey the information that it is immutable.

Annotations are more visible than plain javadoc comments, and you can even use the one from JSR-305 (ex-JCIP). Some static analysis tools can even detect it and verify that your object is not mutated.

Upvotes: 0

Related Questions