Why a generic method of an interface can be implemented as non-generic in Java?
Asked Answered
H

2

14

Let's say we have a few test interfaces/classes like this:

abstract class Plant {
    public abstract String getName();
}

interface Eatable { }

class Apple extends Plant implements Eatable {
    @Override
    public String getName() {
        return "Apple";
    }
}

class Rose extends Plant {
    @Override
    public String getName() {
        return "Rose";
    }
}

interface Animal {
    <T extends Plant & Eatable> void eat(T plant);
}

You can see Animal.eat is a generic method with constraints. Now I have my Human class like this:

class Human implements Animal {
    @Override
    public void eat(Plant plant) {
    }
}

which compiles fine. You can see Human.eat is less constrained than Animal.eat because the Eatable interface is lost.

Q1: Why doesn't the compiler complain about this inconsistency?

Q2: If Plant&Eatable downgrades to Plant is acceptable for the compiler, why it complains on eat(Object plant)?

Hearts answered 10/8, 2016 at 2:49 Comment(7)
Which version of Java are you using. Using Java 1.8 in eclipse gives compile time error as expected.Drumm
@Drumm Java8 in Intellij IDEA.Hearts
@Drumm I have no issue compiling this. ideone.com/7xUcZnViscount
@ElliottFrisch But it happens in compile phase, the compiler knows that, and I think it should gives an error, at least a warning?Hearts
That's exacly what @ElliottFrisch just said.Gaia
Just tried it with javac on the command line, and it doesn't get an error. But if I change Plant to Rose in the parameter list of Human.eat, I now get an error.Encomium
For what it's worth, I can't think of a case where the posted code causes a "problem"--i.e. where code compiles, but at runtime the code attempts to call an Eatable method on an object of a class that doesn't implement Eatable (assuming we added some methods to Eatable).Encomium
B
12

Lesson: Generics by Gilad Bracha according to him

public static <T extends Object & Comparable<? super T>> T max(Collection<T> coll)

This is an example of giving multiple bounds for a type parameter, using the syntax T1 & T2 ... & Tn. A type variable with multiple bounds is known to be a subtype of all of the types listed in the bound. When a multiple bound is used, the first type mentioned in the bound is used as the erasure of the type variable.

so your example <T extends Plant & Eatable> void eat(T plant); will be erased to void eat(Plant plant); so when you override it the compiler doesn't complain

Breaux answered 10/8, 2016 at 3:32 Comment(0)
I
0

Ahmed answer is right,by the way,if you wanna put constraint the implementation of the interface of Animal, you should declare it as this:

interface Animal<T extends Plant & Eatable>  {
    void eat(T plant);
}

Then, if you implements the Animal interface without providing the type information, the compiler will using the least surprise policy to infer the T as a Plant type. But if you provide the necessary type information, the compiler works fine.

class Human implements Animal<Rose> // won't compile
class Human implements Animal<Apple> // compile
Indrawn answered 13/8, 2016 at 7:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.