简体   繁体   中英

How does the java compiler treat missing type arguments for types besides <E>, e.g., <E extends Foo>

I understand the difference between these 3 lines of code,

ArrayList list1 = new ArrayList();
ArrayList<Object> list2 = new ArrayList<Object>();
ArrayList<? extends Object> list3 = new ArrayList<? extends Object>();

but let's say I have this example class Foo,

class Foo<T extends Bar> {
    // ...
}

how does the compiler handle these differently, if at all?

Foo foo1 = new Foo();
Foo<? extends Bar> foo2 = new Foo<? extends Bar>();

I see a lot of examples saying when the type arguments are excluded it doesn't check, but these examples are normally for List examples and the like where anything can be put in ( public interface List<E> extends Collection<E> ). Does the compiler default to the least restrictive type argument in the declaration when it is omitted or does it simply allow anything? Also, if it does default to the declaration, then is there any reason to use the style in foo1 over foo2 ?

Edit: My question is different because it is asking how the compiler treats specific examples and how they differ, the referenced question is about PECS and the difference between extends and super

Let's test this by adding a method in Foo:

class Foo<T extends Bar> {
    void add(T object) {

    }
}

Now let's try the two cases:

Foo genericFoo = new Foo();
genericFoo.add(new Bar());
genericFoo.add(new Object());

Exception in thread "main" java.lang.RuntimeException: Uncompilable source code - Erroneous sym type: stackoverflow.Generics.Foo.add

So it does check to ensure that the generic object is at least of type Bar .


Essentially, these two statements are the same:

Foo<? extends Bar> genericFoo = new Foo<? extends Bar>();
Foo genericFoo = new Foo();

The advantage of the former is that it makes it clearer that you have to add Bar type objects to it.

The best way to write it would probably be like this:

Foo<Bar> genericFoo = new Foo<>();

You can still add children of Bar here, and it's clear that the generic type must be a Bar .

TL;DR

They are the same.

Long Form

This is a simple test case. In Eclipse there is an error on both put calls. Saying that the method is not applicable for the arguments.

public class Main {

   public static void main(final String[] args) {

      final Test test1 = new Test();
      final Test<? extends String> test2 = new Test<? extends String>();

      test1.put(new Integer(1));
      test2.put(new Integer(1));
   }

   private static class Test<T extends String> {

      public void put(final T value) { }

   }
}

The compiler gives the same error when either is commented out and they are compiled individually.

Exception in thread "main" java.lang.Error: Unresolved compilation problems: 
  The method put(String) in the type Main.Test is not applicable for the arguments (Integer)
  The method put(capture#1-of ? extends String) in the type Main.Test<capture#1-of ? extends String> is not applicable for the arguments (Integer)

  at Main.main(Main.java:9)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM