skiwi
skiwi

Reputation: 69259

Why do my generics not take a subclass?

I had a question about similar generics yesterday, and as solution I implemented a sort of self-reference in some classes, like this:

public interface Action { }

public interface Result { }

public interface Player<A extends Action, R extends Result, P extends Player<A, R, P>> {
    default public void onPostAction(final P target, final A action, final R result) { }
}

abstract public class GesturePlayer<A extends Action, R extends Result, P extends GesturePlayer<A, R, P>> implements Player<A, R, P> { }

abstract public class RPSPlayer extends GesturePlayer<RPSGesture, RPSResult, RPSPlayer> { }

public class RPSHumanPlayer extends RPSPlayer {
    @Override
    public void onPostAction(final RPSHumanPlayer target, final RPSGesture gesture, final RPSResult result) { }
}

This code does not compile, hoewver I am unable to figure out why.
It does work if in the @Override I use RPSPlayer, however RPSHumanPlayer is simply a subclass of it, should it not work the same as the following?

List<T> list = new ArrayList<>();

Which also has the type definied as the superclass (List resp RPSPlayer), and the referenced object's type as the subclass (ArrayLast resp RPSHumanPlayer).

My aim with this question is to gather insight on how the generics exactly work, and I want to keep the method signature as it is defined in RPSHumanPlayer.

What I think I understand about generics:

This code is written on Java 8.

Upvotes: 1

Views: 135

Answers (2)

Your problem boils down to this:

public interface Player {
    default public void onPostAction(Player target) {}
}

public abstract class HumanPlayer implements Player {
    @Override
    public void onPostAction(HumanPlayer target) {}
}

This cannot work, because onPostAction(HumanPlayer) cannot override onPostAction(Player), because then what would happen if it was called with a Player that was not a HumanPlayer?

Upvotes: 1

Ray
Ray

Reputation: 3201

In order to achieve the desired method signature in RPSHumanPlayer, you will need to generify RPSPlayer like this:

abstract public class RPSPlayer<P extends RPSPlayer<P>> extends GesturePlayer<RPSGesture, RPSResult, P> { }

Then you can define:

public class RPSHumanPlayer extends RPSPlayer<RPSHumanPlayer>

In Java, parameter types are part of the method signature, so they can't be changed (not even subclassed). Since Java 5, you can use covariant return types, but that's as far as it goes.

Upvotes: 2

Related Questions