1

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
Community
  • 1
  • 1
Xtra Coder
  • 2,891
  • 2
  • 30
  • 52

1 Answers1

2

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.

Community
  • 1
  • 1
JB Nizet
  • 633,450
  • 80
  • 1,108
  • 1,174
  • Can you tell more about what you mean by 'unneeded complexity'? I'm asking because for me declaring hundreds of variables as `ListOfAtLeastBaseClasses` is more `complex` than as something like `ListOfAtLeastBaseClasses` or `ListOfAtLeastBaseClasses<>`. Shortly - current diamond operator removes type-definition redundancy during creating of instances. I think same elimination of redundancy for 'definition of variables' would reduce complexity of code. – Xtra Coder Dec 05 '16 at 05:14
  • Most usages of generic types consist in using a type that is more specific than the upper bound (otherwise there would be little point in making the type generic in the first place). For example: I can't remember the last time I used a List. Adding yet another syntax would make generics more complex and wouldn't add anything to what we can already do now. The diamond operator is different, because it solves a verbosity problem that is very, very common. But anyway, neither you nor I will be able to change the language, so... – JB Nizet Dec 05 '16 at 07:02
  • Not to mention that your proposal would make the code less readable. To know the actual type of foo in `Foo foo = someMethod()` I would have to look at the type definition of Foo to know what the upper bound is. – JB Nizet Dec 05 '16 at 07:06
  • I think the same time you'd define base list class you'd give it better name than just `Foo` - e.g. `interface FooItems extends List extends FooItem> {}` and then its usage would be (as variable) `FooItems items = getFooItems();` or (as argument) `void doSomthingWithFooItems(FooItems items)`. I see no problem with 'readbility' here. Currently i have to redundantly explicitly define generic parameter `FooItems items = getFooItems();` or (as argument) `void doSomthingWithFooItems(FooItems items)`. Isn't it too verbose? – Xtra Coder Dec 06 '16 at 07:31