What does Java's type parameter wildcard really mean? What's the real difference between Foo and Foo<?>?

后端 未结 5 972
清酒与你
清酒与你 2021-01-05 02:32

For a generic interface:

public interface Foo {
    void f(T t); 
} 

The difference between the two fields:

publ         


        
相关标签:
5条回答
  • 2021-01-05 02:47

    Consider these types:

    • List<Object>
    • List<CharSequence>
    • List<String>

    Even though String is a subtype of CharSequence which is a subtype of Object, these List types do not have any subtype-supertype relationships. (Curiously, String[] is a subtype of CharSequence[] which is a subtype of Object[], but that's for historical reasons.)

    Suppose we want to write a method which prints a List. If we do

    void print(List<Object> list) {...}
    

    this will not be able to print a List<String> (without hacks), since a List<String> is not a List<Object>. But with wildcards, we can write

    void print(List<?> list) {...}
    

    and pass it any List.

    Wildcards can have upper and lower bounds for added flexibility. Say we want to print a list which contains only CharSequences. If we do

    void print(List<CharSequence> list) {...}
    

    then we encounter the same problem -- we can only pass it a List<CharSequence>, and our List<String> is not a List<CharSequence>. But if we instead do

    void print(List<? extends CharSequence> list) {...}
    

    Then we can pass this a List<String>, and a List<StringBuilder>, and so forth.

    0 讨论(0)
  • 2021-01-05 02:47

    Object is the supertype of all types in java. However, Foo is not the supertype of all Foo. The supertype of all Foo is Foo. see http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html for more details.

    0 讨论(0)
  • 2021-01-05 02:48

    The wildcard in Foo<?> indicates that within the current scope, you don't know or care what type of 'Foo' you have.

    Foo<?> and Foo<? extends Object> are the same (the first is shorthand for the other). Foo<Object> is different.

    A concrete example:

    You can assign any sort of List to List<?> e.g.

    List<?> list1 = new ArrayList<String>();
    List<?> list2 = new ArrayList<Object>();
    List<?> list3 = new ArrayList<CharSequence>();
    

    If you have a List<?> you can call size() because you don't need to know what type of list it is to find out its size. And you can call get(i) because we know that the list contains some sort of Object, so the compiler will treat it as if get returns and Object.
    But you can't call add(o) because you don't know (and the compiler doesn't know) what sort of list you're dealing with.
    In our example above you wouldn't want to allow list1.add(new Object()); because that's supposed to be a list of Strings

    The reason for wildcards is so you can do things like this:

    public static boolean containsNull(List<?> list)
    {
        for(Object o : list )
        {
           if( o == null ) return true;
        }
        return false;
    }
    

    That code can work on any sort of list that you want, a List<String>, List<Object>, List<Integer>, etc.

    If the signature was public static boolean containsNull(List<Object> list) then you could only pass List<Object> to it, List<String> wouldn't work.

    0 讨论(0)
  • 2021-01-05 03:05

    Foo<?> is semantically the same as Foo<? extends Object>: it is a Foo with type parameter of something specific, but the only thing known about "something" is that it is some subclass of Object (which isn't saying too much, since all classes are subclasses of Object). Foo<Object>, on the other hand, is a Foo with type parameter specifically Object. While everything is assignment-compatible with Object, not everything will be assignment-compatible with ? where ? extends Object.

    Here's an example of why Foo<?> should generate an error:

    public class StringFoo implements Foo<String> {
        void foo(String t) { . . . }
    }
    

    Now change your example to this:

    Foo<?> foo1 = new StringFoo();
    

    Since i is an Integer, there's no way that the compiler should allow foo1.foo(i) to compile.

    Note that

    Foo<Object> foo4 = new StringFoo();
    

    will also not compile according to the rules for matching parameterized types since Object and String are provably distinct types.

    Foo (without type parameter at all—a raw type) should usually be considered a programming error. According to the Java Language Specification (§4.8), however, the compiler accepts such code in order to not break non-generic, legacy code.

    Because of type erasure, none of this makes any difference to generated the byte code. That is, the only differences between these are at compile time.

    0 讨论(0)
  • 2021-01-05 03:10

    Well, due to the type erasure, anything generics-related is compile-time only; I guess that's what you are calling syntactical.
    I think your initial assessment is correct and real difference is that makes it a generic-typed variable and plain Foo doesn't.

    0 讨论(0)
提交回复
热议问题