Xtra Coder
Xtra Coder

Reputation: 3497

Is 'Diamond operator' missing for definition of variables in Java?

I've got back to Java from C# few years ago when Java7 was already released for year or so - java generic seem weird to me in some aspects.

There is the one I'm coming back over and over again and seeing popularity of these two answers ...

  1. Difference between <? super T> and <? extends T> in Java
  2. What is PECS (Producer Extends Consumer Super)?

... it seems many people (including me) 'naturally' would expect different behavior from syntax in declarations of 'generic collections'. Considering code sample below ...

Note: architectural correctness is out of scope - it is assumed that baseClassesStorage should be available for both get/add in external world

public class DummyClass {

    public static class BaseClass {}

    public static interface ListOfAtLeastBaseClasses<T extends BaseClass> extends List<T> {}

    public ListOfAtLeastBaseClasses<?> baseClassesStorage;

    public void addToStorage(BaseClass item) {
        baseClassesStorage.add(item);
    }
}

... typical programmer from non-java world would suppose that ListOfAtLeastBaseClasses<?> baseClassesStorage say "this is the storage for items derived from BaseClass", but compilation of this sample fails at baseClassesStorage.add(item) with the error below:

C:\Temp\...\DummyClass.java:14: error: no suitable method found for add(BaseClass)
        baseClassesStorage.add(item);
                          ^
    method Collection.add(CAP#1) is not applicable
      (argument mismatch; BaseClass cannot be converted to CAP#1)
    method List.add(CAP#1) is not applicable
      (argument mismatch; BaseClass cannot be converted to CAP#1)
  where CAP#1 is a fresh type-variable:
    CAP#1 extends BaseClass from capture of ?

Reason for such behavior is described in two above mentioned answers.

But what should I do to implement my use-case in a both 'correct' and 'elegant' way?

Options i'm aware about:

  1. It seems that 'correct Java way' is declaring variable as public ListOfAtLeastBaseClasses<BaseClass> baseClassesStorage; but this way I actually have redundant definition of the lower bound of that 'List' - it is already defined in 'contract' of ListOfAtLeastBaseClasses class. Another problem here - if at some point of time I would want to change lower bound limit for ListOfAtLeastBaseClasses class, then I will need to go through all member variables of this type and change their definition :(.

  2. Use 'raw' types public ListOfAtLeastBaseClasses baseClassesStorage; this will compile, but efficiently removes type checks and generates unwanted warnings during build.

  3. ... what else is possible ? ...

    • ListOfAtLeastBaseClasses<?> baseClassesStorage; - is a different thing in Java
    • ListOfAtLeastBaseClasses<> baseClassesStorage; - compile error
    • ListOfAtLeastBaseClasses<*> baseClassesStorage; - compile error

Upvotes: 1

Views: 232

Answers (1)

JB Nizet
JB Nizet

Reputation: 691625

It seems that 'correct Java way' is declaring variable as

public ListOfAtLeastBaseClasses<BaseClass> baseClassesStorage;

but this way I actually have redundant definition of the lower bound of that 'List'

Not really. When you define your generic type, you're saying that users of this class will be able to declare variables like

ListOfAtLeastBaseClasses<Foo> listOfFoo;
ListOfAtLeastBaseClasses<Bar> listOfBar;

where Foo and Bar must be subclasses of BaseClass. BaseClass is an upper bound. But users of the generic type can choose the list to be more restrictive, and thus accept only instances of Foo or Bar.

When you declare your list as

ListOfAtLeastBaseClasses<BaseClass>

you choose to make a list that can accept any kind of BaseClass instance.

We could imagine a special syntax to say ListofAtLeastBaseClasses<TheDefinedUpperBound>, but that's not what the diamond operator is for, and that special syntax would, IMO, introduce unneeded complexity.

Upvotes: 2

Related Questions