RFC: draft API for JEP 269 Convenience Collection Factories

Timo Kinnunen timo.kinnunen at gmail.com
Mon Oct 19 14:40:22 UTC 2015


Having one less import-statement that has to be written -- while editing a file -- by Content Assist/IntelliSense/Code Completion is a small improvement, but apart from that going from:
	ArrayList<String> als = new ArrayList<>(Arrays.asList("a", "b", "c"));
to:
	ArrayList<String> als = new ArrayList<>(List.of("a", "b", "c"));

may otherwise be a regression. Arrays.asList() isn't immutable so it can just wrap the array without copying, but an immutable List.of() has to make a defensive copy. The ArrayList constructor can't easily pick up the slack either, it needs to create a copy too. Otherwise stashing a reference as in the following could be used to create a mutable immutable List:

	String[] mutableArray;
	List<String> immutableList;
	ArrayList<String> als = new ArrayList<>(immutableList  = List.of(mutableArray = new String[] { "a", "b", "c" }));

Then again a mutable List looking like an immutable List can also be easily created with:
	List<String> immaybemutableList = new ArrayList<>(Arrays.asList("a", "b", "c"));

and I'm left wondering how much utility is in a truly immutable List that no-one else can freely share trusting it to be immutable. Having to sprinkle some List.ensureImmutable(immaybemutableList) method calls everywhere would be pretty bad.





Sent from Mail for Windows 10



From: Peter Levart
Sent: Sunday, October 18, 2015 20:42
To: Stuart Marks;Remi Forax
Cc: core-libs-dev
Subject: Re: RFC: draft API for JEP 269 Convenience Collection Factories




On 10/17/2015 06:46 PM, Stuart Marks wrote:
>
>
> On 10/10/15 6:55 AM, Remi Forax wrote:
>> There is an issue with LinkedHashMap (resp LinkedHashSet),
>> it inherits from HashMap /facepalm/, and static methods are 
>> accessible through class inheritance /facepalm/.
>> So if LinkedHashMap doesn't declare some methods of(),
>>    LinkedHashMap.of("foo")
>> will return a HashMap :(
>
> Hi Rémi,
>
> Thanks for pointing this out. I had forgotten about "inheritance" of 
> static methods. /Facepalm/ indeed. The original proposal (July 2014) 
> avoided this issue by adding static methods only on interfaces, which 
> are *not* inherited. When we added statics on concrete classes this 
> problem returned.
>
> (I view calling an "inherited" class static method to be poor coding 
> style, but neither javac nor NetBeans warns about it.)
>
> Well, perhaps we won't be adding static factories to the concrete 
> collections after all, so maybe we can dodge this issue. See messages 
> downthread.
>
> s'marks
>

Concrete classes have constructors. With diamonds, if you are lucky. ;-)

Why not adding some constructors like:

public class ArrayList<E> {
     // existing
     public ArrayList(int initialCapacity)
     public ArrayList()
     public ArrayList(Collection<? extends E> c)

     // new
     public ArrayList(E e1)
     public ArrayList(E e1, E e2)
     public ArrayList(E e1, E e2, E e3)
     ...
     public ArrayList(E... es)


There might be some problems with a few cases like:

List<Integer> list1 = new ArrayList<>(42); // empty list with capacity 42
List<Integer> list2 = new ArrayList<>((Integer) 42); // list with single 
element

And consequently some source incompatibilities in rare occasions. But 
would that hurt too much?

The question is, and that applies to .of() static methods too, whether 
there should be overloads that take 'initialCapacity' too. The 
programmer's choice of mutable collection (instead of immutable one) is 
probably a consequence of expanding the collection later. So pre-sizing 
it to the number of initial elements is rarely optimal.

Mutable collections maybe just need a set of instance methods (defaults 
on Collection with covariant overrides on implementations?) that are 
used to add a bunch of elements and return 'this', like:

public class ArrayList<E> {

     public ArrayList<E> addElements(E e1)
     public ArrayList<E> addElements(E e1, E e2)
     public ArrayList<E> addElements(E e1, E e2, E e3)
     ...
     public ArrayList<E> addElements(E ... es)


Unfortunately, diamonds are not possible in such arrangement as javac 
infers ArrayList<Object> for the following:

         ArrayList<String> als = new ArrayList<>().addElements("a", "b", 
"c");

Error:(16, 61) java: incompatible types: ArrayList<java.lang.Object> 
cannot be converted to ArrayList<java.lang.String>

So it would have to be written explicitly as:

         ArrayList<String> als = new 
ArrayList<String>().addElements("a", "b", "c");

The question is also whether initialization expressions with mutable 
collections and initial sets of elements would really be that frequent 
in practice. Currently we can use this:

         ArrayList<String> als = new ArrayList<>(Arrays.asList("a", "b", 
"c"));

If it can be changed to this:

         ArrayList<String> als = new ArrayList<>(List.of("a", "b", "c"));

...it's an improvement.

Regards, Peter






More information about the core-libs-dev mailing list