RFC: draft API for JEP 269 Convenience Collection Factories

Peter Levart peter.levart at gmail.com
Sun Oct 18 18:41:33 UTC 2015



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