maks
maks

Reputation: 6006

Generics and type inference in Java

I am reading generics chapter in "Thinking in Java 4th edition" and there is an example:

class Fruit{}
class Apple extends Fruit {}   

...

static <T> void writeExact(List<T> list, T item) {
            list.add(item);
    }

    static List<Apple> apples = new ArrayList<>();
        static List<Fruit> fruit = new ArrayList<>();

        static void f1() {
            writeExact(apples, new Apple());
            // writeExact(fruit, new Apple()); // Error:
            // Incompatible types: found Fruit, required Apple
        }

        static <T> void writeWithWildcard(List<? super T> list, T item) {
            list.add(item);
        }

        static void f2() {
            writeWithWildcard(apples, new Apple());
            writeWithWildcard(fruit, new Apple());
        }

Those commented line, where error is indicated, doesn't produce any error at all both in 6 and 7 Java. It seems weird for me, as writeExact method accepts only exact type of parameter. So why does it work? And since it works what is a purpose of supertype wildcards?

edit

so i got it and want to clarify one more thing: who is the boss in type inference with wildcards? return type, first parameter, second parameter, ... . I mean that if SomeClass.<Fruit>method(...) is not specified

Upvotes: 3

Views: 252

Answers (2)

Tom Anderson
Tom Anderson

Reputation: 47243

It works because the T in writeExact is a type variable on the method itself. Look, it's right there before the void return type:

static <T> void writeExact(List<T> list, T item)

That means that whenever you call it, its T takes on the type needed to accept its arguments, as long as there is one. You can call it with any list and an object that would fit in that list.

Perhaps it makes it clearer to explicitly give the type argument, rather than letting the compiler infer it (you need to use the class's name to do this for a static method; i will assume it is MyClass):

        MyClass.<Apple>writeExact(apples, new Apple());
        MyClass.<Fruit>writeExact(fruit, new Apple());

In the second call, T has the value Fruit, which means the first argument must be a List<Fruit>, which it is, and the second must be a Fruit, which it is, because Apple is a subtype of Fruit.

The rules for implicitly assigning type arguments are given in section 15.12.2.7 of the Java Language Specification. At least, i think they are; it's not exactly an easy read.

I would guess that the code in the book is not exactly the same as the code you give above. If it is, then it it definitely incorrect, and you should write to Mr Eckel and tell him.

Upvotes: 2

PermGenError
PermGenError

Reputation: 46438

static <T> void writeExact(List<T> list, T item) {
            list.add(item);
    }

would accept (List of Applies,Apple or List of Fruits, Fruit or List of Applles, Fruit(as Fruit is the subtype of apple)) because you declared it with parameterized types T.

Upvotes: 1

Related Questions