I am creating a game where objects implement interfaces for animations. I have a parent interface for the animations. Here is a shortened version:
public interface Animates<S extends Animator> {
S createAnimator(long animationTime);
}
In addition, I have multiple interfaces that extend this interface. Two examples:
public interface AnimatesPaint extends Animates<PaintAnimator> {
PaintAnimator createPaintAnimator(long animationTime);
default PaintAnimator createAnimator(long animationTime) {
return createPaintAnimator(animationTime);
}
}
and
public interface AnimatesPosition extends Animates<PositionAnimator> {
PositionAnimator createPositionAnimator(long animationTime);
@Override
default PositionAnimator createAnimator(long animationTime) {
return createPositionAnimator(animationTime);
}
}
As you can see, the interfaces that extend Animates
override the createAnimator
method in order to delegate the logic of createAnimator
to the class that implements the interface.
The reason I am doing this is that I want to be able to have a screen element (that can animate) that implements multiple animation interfaces (for example both AnimatesPosition
to move the element around and AnimatesPaint
to change its color).
However, that apparently does not work. When I implement both in one class (illustrated below), I get the compilation error:
createAnimator(long) in AnimatesPaint clashes with createAnimator(long) in AnimatesPosition; attempting to use incompatible return types
Here is an example of a class that implements both Animates interfaces:
public class ScreenElement implements AnimatesPaint, AnimatesPosition {
@Override
PositionAnimator createPositionAnimator(long animationTime) {
return new PositionAnimator(animationTime);
}
@Override
PaintAnimator createPaintAnimator(long animationTime) {
return new PaintAnimator(animationTime);
}
}
So what I don't understand is that both AnimatesPaint
and AnimatesPosition
already implement createAnimator
. Yet, the error message seems to suggest that createAnimator
also needs to be implemented by ScreenElement
! If createAnimator
would not have been implemented yet, I would get it, why there is a clash.
Where does my logic go wrong?
In the end, what I want to achieve, is to have a generic method that can initiate any type of animation. For example:
public class AnimationStarter<S extends Animates, T> {
public void startAnimation(S animates, T start, T target, long animationTime) {
Animator animator = animates.createAnimator(animationTime);
animator.init(start, target);
animates.setAnimator(animator);
animator.startAnimation();
}
}
--Edit--
Upon request, here is the declaration of Animator
public abstract class Animator<T> {}
and one of its extended classes
public class PositionAnimator extends Animator<Point>{}
So what I don't understand is that both
AnimatesPaint
andAnimatesPosition
already implementcreateAnimator
.
Yes, and those implementations conflict with one another. If you could do this, your resulting class's type would need to expose two createAnimator
methods that are only differentiated by return type. Java doesn't let you have overloads that are only differentiated by return type, so you can't do that. A method signature, for overloading purposes, doesn't include the return type.
Even if they had the same return type (Animator
), you'd then have two overloads with exactly the same signature, which you can't do.
They'll need to be separate methods — e.g., with separate signatures that can be differentiated — if they're going to be implemented in the same class.
In a comment you've asked:
But isn't the conflict resolved by the fact that the method has already been overriden by
AnimatesPaint
andAnimatesPosition
? This way the implementing classScreenElement
doesn't need to implementcreateAnimator
method, so no conflict will occur.
No, because the class itself exposes those methods (or rather, would need to) as part of its signature. Basically, suppose you could create the class and you had an instance of it, s
. What would s.createAnimator(300L)
do? Which one should the compiler choose?
A class's public type is composed of all of its public members, including all the public members of all of the interfaces it implements. So at a type level, it's impossible for two interfaces to implement methods with the same signature.