Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java wildcard in multi-level generic type

Why Bar.go is OK with argument f2 but not with argument f1?

public class HelloWorld {
    public static void main(String[] args) {
        Foo<Foo<?>> f1 = new Foo<Foo<?>>();
        Foo<Foo<String>> f2 = new Foo<Foo<String>>();
        Bar.go(f1);     // not OK
        Bar.go(f2);     // OK
    }

    public static void p(Object o) {
        System.out.println(o);
    }
}

class Foo<E> {
}

class Bar {
    public static <T> void go(Foo<Foo<T>> f) {
    }
}

Shouldn't the compiler automatically infer type T as capture of ? in both cases?

like image 914
Cyker Avatar asked Sep 23 '12 03:09

Cyker


People also ask

Which is the wildcard symbol used for generic type specification?

The question mark (?) is known as the wildcard in generic programming. It represents an unknown type.

What is wildcard in Java generic?

In generic code, the question mark (?), called the wildcard, represents an unknown type. The wildcard can be used in a variety of situations: as the type of a parameter, field, or local variable; sometimes as a return type (though it is better programming practice to be more specific).

Why do we need wildcard in generics in Java?

We can use the Java Wildcard as a local variable, parameter, field or as a return type. But, when the generic class is instantiated or when a generic method is called, we can't use wildcards. The wildcard is useful to remove the incompatibility between different instantiations of a generic type.

What is the difference between bounded and unbounded wildcards in Java generics?

both bounded and unbounded wildcards provide a lot of flexibility on API design especially because Generics is not covariant and List<String> can not be used in place of List<Object>. Bounded wildcards allow you to write methods that can operate on Collection of Type as well as Collection of Type subclasses.


2 Answers

Great question!

(In the following comments, w.r.t. a class generic in E like Foo< E >, define "covariant method" as a method that returns an E without having any parameters using E, and a "contravariant method" as the opposite: one which takes a formal parameter of type E but doesn't return a type involving E. [The real definition of these terms is more complicated, but never mind that for now.])

It seems that the compiler is trying to bind T to Object in the case of f1, because if you do

class Bar0 {
    public static < T > void go( Foo< Foo< ? extends T > > f ) {
        // can pass a Foo< T > to a contravariant method of f;
        // can use any result r of any covariant method of f,
        // but can't pass T to any contravariant method of r
    }
}

then the go(f1) works, but now go(f2) doesn't, because even though Foo< String > <: Foo< ? extends String >, that does not imply that Foo< Foo< String > > <: Foo< Foo< ? extends String > >.

Here are a few modifications that compile for both f1 and f2:

class Bar1 {
    public static < T > void go( Foo< ? super Foo< T > > f ) {
        // can't properly type the results of any covariant method of f,
        // but we can pass a Foo< T > to any contravariant method of f
    }
}

class Bar2 {
    public static < T > void go( Foo< ? extends Foo< ? extends T > > f ) {
        // can't pass a Foo< T > to a contravariant method of f;
        // can use result r of any covariant method of f;
        // can't pass a T to a contravariant method of r;
        // can use result of covariant method of r
    }
}
like image 180
Judge Mental Avatar answered Sep 22 '22 11:09

Judge Mental


Foo<Foo<?>> f1 = new Foo<Foo<?>>();

This implies that the type is unknown and objects of any type can be added to Foo<Foo<?>> that are heterogeneous and compiler cannot guarantee that all object in Foo<Foo<?>> are of same type. Hence it cannot be passed to Bar.go that takes a bounded type as parameter.

You can instead declare that as Foo<Foo<Object>> f1 = new Foo<Foo<Object>>(); to pass it to Bar.go where you explicitly mention everything is of type Object.

like image 31
Vikdor Avatar answered Sep 24 '22 11:09

Vikdor