Reputation: 3497
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 ...
... 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:
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 :(.
Use 'raw' types
public ListOfAtLeastBaseClasses baseClassesStorage;
this will compile, but efficiently removes type checks and generates unwanted warnings during build.
... what else is possible ? ...
ListOfAtLeastBaseClasses<?> baseClassesStorage;
- is a different thing in JavaListOfAtLeastBaseClasses<> baseClassesStorage;
- compile errorListOfAtLeastBaseClasses<*> baseClassesStorage;
- compile errorUpvotes: 1
Views: 232
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