Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

For a generic interface:

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

The difference between the two fields:

public class Bar {
    Foo foo1; 
    Foo<?> foo2; 
}

Is that foo2 is a generic Type and foois not. Since ? is a wildcard (which I think means any type) and every type is a sub-type of Object, then I wold expect Foo<?> and Foo<Object> to semantically and syntactically equivalent.

However, check out the following:

public class Puzzler {
    void f() {
        Integer i = null; 
        Foo<?> foo1 = null;
        foo1.foo(i); // ERROR 
        Foo foo2 = null; 
        foo2.foo(i); // OKAY
        Foo<Integer> foo3 = null; 
        foo3.foo(i); // OKAY 
        Foo<Object> foo4 = null; 
        foo4.foo(i); // OKAY
    }

    private interface Foo<T> {
        void foo(T t);
    } 
}

So Foo<?> and Foo<Object> are not the same syntactically.

What's going on here? I'm pretty stuck in trying to understand this.

like image 822
mwsltn Avatar asked Mar 07 '12 01:03

mwsltn


People also ask

What is the difference between type parameters and wildcards?

The difference is that if you have a type parameter U, you can use that type inside the method; if you use a wildcard, you don't have access to the actual type inside the method (you only know that it is some unknown type that extends Number).

What is a wildcard parameter?

In the Java programming language, the wildcard ? is a special kind of type argument that controls the type safety of the use of generic (parameterized) types. It can be used in variable declarations and instantiations as well as in method definitions, but not in the definition of a generic type.

Where can you use a wildcard (?) To denote a parameter type in your code?

The question mark (?) is known as the wildcard in generic programming. It represents an unknown type. The wildcard can be used in a variety of situations such as the type of a parameter, field, or local variable; sometimes as a return type.

What is the difference between E and T?

Well there's no difference between the first two - they're just using different names for the type parameter ( E or T ).


2 Answers

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.

like image 63
Ted Hopp Avatar answered Nov 02 '22 23:11

Ted Hopp


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.

like image 28
Daniel Lubarov Avatar answered Nov 02 '22 23:11

Daniel Lubarov