Search code examples
javagenericsbounded-types

Bounded type parameters: cannot access subtype methods when overriding


I'm new to using bounded types in Java, and I'm not sure if the following is a programming error because of a bad use of inheritance or a javac bug


I need to define two different type of objects: things which have to be managed and managers of that things. That's why I created an abstract class to model the common behavior of those things

public abstract class AbstractThing {
    // Common method implemented
    public void hello() {
        System.out.println("HI, I'm AbstractThing");
    }
} 

and an interface to define the methods that those things' manager must implement

public interface AbstractManager {
    // Operation that a things' manager must implement
    public <T extends AbstractThing> void greet(T t);
}

So suppose I create two concrete things classes, one of them just inherits the abstract one:

public class Thing extends AbstractThing {
    // Constructor
    public Thing() {}
}

but the other one implements an own method:

public class AnotherThing extends AbstractThing {    
    // Constructor
    public AnotherThing() {}
    
    // Extra method which this class implements
    public void goodbye() {
        System.out.println("BYE, I'm AnotherThing");
    }
}

But when I define a manager as follows:

public class Manager implements AbstractManager {
    
    // Constructor method
    public Manager() {}

    // Implementation of the interface's method fails
    @Override
    public <AnotherThing extends AbstractThing>
    void greet(AnotherThing t) {
        // I can use this method, which AnotherThing inherits from AbstractThing
        t.hello();
        // But I can't use this one defined by AnotherThing
        t.goodbye();
    }

}

I get the error:

AnotherManager.java:15: error: cannot find symbol
        t.goodbye();
         ^
  symbol:   method goodbye()
  location: variable t of type AnotherThing
  where AnotherThing is a type-variable:
    AnotherThing extends AbstractThing declared in method <AnotherThing>greet(AnotherThing)
1 error

And I don't understand why, because it's recognizing the class as AnotherThing, but it's dealing it as AbstractThing. I've tried to cast the object as the subclass, but it doesn't work

I've also checked that it only happens when I try to access the subtype methods, because the following manager compile and works perfectly:

public class Manager implements AbstractManager {

    // Constructor method
    public Manager() {}
 
    // Implementation of the method defined into the interface
    @Override
    public <Thing extends AbstractThing>
    void greet(Thing t) {
        t.hello();
    }

    // I can overload the method "greet" without overriding the interface 
    // and it works for AnotherThing
    public void greet(AnotherThing t) {
        t.hello();
        t.goodbye();
    }

}

Any idea about what's happening there?


Solution

  • Here's why

    enter image description here

    It's not difficult to figure out if you do something like this

    public class Manager implements AbstractManager {
        
        // Constructor method
        public Manager() {}
    
        // Implementation of the interface's method fails
        @Override
        public <T extends AbstractThing>
        void greet(T t) {
            // I can use this method, which T inherits from AbstractThing
            t.hello();
            // But I can't use this one because T inherits from AbstractThing which does not know this method
            t.goodbye();
        }
    
    }
    

    In other words, the word before the "extends" is not supposed to be a class name, but a generic identifier (a variable name).

    In this case, since T (or wharever you call it) is AbstractThing, it does not know what goodbye() is.

    Now, why your last example works?

    Because you're saying that greet receives a object of type AnotherThing, not a generic type such as T.