0

I was trying some sample programs on Generics Upper/Lower bounds.. Generics Upper Bound is giving compilation error...But Lower Bound is fine. I am just trying to put a List of type T into a set and try both Upper and Lower bound scenarios..

Please help to identify the issue with testUpperBound(T t) method and why exactly does the testLowerBound(T t) method compile and the testUpperBound(T t) one doesn't. I checked other similar threads..But still I haven't got it clear.

Please Let me know if need more details .

 public class TestGenerics<T> 
    {

        public static void main(String...args)
        {
           List<String> list = new ArrayList<>();
           list.add("New ArrayList");
           new TestGenerics<List<String>>().testUpperBound(list);
           new TestGenerics<List<String>>().testLowerBound(list);

        }

       public  void testLowerBound(T t)
       {
            Set<? super ArrayList<T>> lowerBoundSet = new HashSet<>();
            lowerBoundSet = new HashSet<List<T>>();
            ArrayList<T> list = new ArrayList<>();
            list.add(t);
            lowerBoundSet.add(list);  // compiles..

            out.println(lowerBoundSet);
        }

        public  void testUpperBound(T t)
        {
            Set<? extends List<T>> upperBoundSet = new HashSet<>();
            upperBoundSet = new HashSet<List<T>>();
            ArrayList<T> list = new ArrayList<>();
            list.add(t);
            upperBoundSet.add(list);  // Doesn't compile..

            out.println(upperBoundSet);
        }

    }

3 Answers3

0

You can not modify a collection parameterized with <? extends SomeType>. Java just does not allow that as it is not safe action. add() modifies the collection, so you can't do it. There is no such restriction for <? super SomeType>

Yaroslav Rudykh
  • 803
  • 6
  • 12
  • Thanks for answering Yaroslav... But could you please help to find what exactly is the difference between lower and upper methods in my code where lower one compiles and upper one doesn't ? – Santhosh Kaitheri Nov 20 '16 at 11:51
  • Uh not quite, you cannot _insert_ into a `Collection extends T>` but you can insert into a `Collection super T>`, and vice-versa (can retrieve from ` extends T>` and cannot retrieve from a ` super T>`). Iirc you can still retrieve from a ` super T>` with casting but in general it's an extension of PECS – Rogue Nov 20 '16 at 12:00
  • As for the reason behind the restriction... It relates to type erasure. Java doesn't know the exact parametrization in runtime so modification is potentionally dangerous operation for upper bounded collections. There is a lot of examples of what could go wrong if there were no such restriction in the Internet. – Yaroslav Rudykh Nov 20 '16 at 12:01
  • Thanks for your patience friends.. :) . Still can we have one example here.. ? :) . I have "Effective Java" with me ...But still somehow not getting the answer for this 100% clear.. :( – Santhosh Kaitheri Nov 20 '16 at 12:06
  • The duplicate target already has examples, please check them out. – Nathan Hughes Nov 20 '16 at 13:06
  • Thank You All for explaining. I got it. – Santhosh Kaitheri Nov 20 '16 at 16:33
0

Here you have your answer :

Explanation of the get-put principle

It is java rule, that's all. I may give you an example with your code why it would be unsafe if compilation passed :

public void extendsExample(){
  Set<? extends List<? extends String>> setOfList = new HashSet<>();
  Set<ArrayList<String>> setOfArrayList = new HashSet<>();

  // now setOfList var refers to a set 
  // which contains a arraylist of String
  setOfList = setOfArrayList;

  // compilation fails
  setOfList.add(new LinkedList<String>());
 }

Imagine the compilation doesn't fail.
It means setOfArrayList instance which is a set which should contain ArrayList instances, contains now a list of LinkedList element.

If you iterate on setOfArrayList you will not have exclusively ArrayList<String> elements as expected. It's no safe and that's why the compilation fails.

Here the example with <? super :

public void superExample(){
  Set<? super ArrayList<String>> setOfArrayList = new HashSet<>();

  // compilation ok
  setOfArrayList.add(new ArrayList<String>());
  // new anonymous type derivating from ArrayList
  ArrayList<String> derivedArrayList = new ArrayList<String>(){
  };
  // compilation ok
  setOfArrayList.add(derivedArrayList);
}
Community
  • 1
  • 1
davidxxx
  • 125,838
  • 23
  • 214
  • 215
0

Simply put, we do not know at compile time what type of lists are contained in the upperBoundSet. It could be a Set<ArrayList<T>>, or it could be a Set<LinkedList<T>>, or it could be one of many other alternatives.

If it turns out to be a Set<LinkedList<T>>, then adding an ArrayList to it is obviously a bad idea. But because we don't know what type the set's contents are, it takes the safer option and blocks it.

Joe C
  • 15,324
  • 8
  • 38
  • 50