6

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>{}
1
  • Can you also show the declaration of Animator? Commented Oct 12, 2019 at 10:06

2 Answers 2

4

So what I don't understand is that both AnimatesPaint and AnimatesPosition already implement createAnimator.

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 and AnimatesPosition? This way the implementing class ScreenElement doesn't need to implement createAnimator 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.

Sign up to request clarification or add additional context in comments.

6 Comments

But isn't the conflict resolved by the fact that the method has already been overriden by AnimatesPaint and AnimatesPosition? This way the implementing class ScreenElement doesn't need to implement createAnimator method, so no conflict will occur.
@MWB - No, because the class itself exposes those methods (or rather, would need to). 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?
So what is the solution? Removing the createAnimator method in the Animates interface and adding a parameter in the startAnimation method to decide which type of Animator (using createPaintAnimator or createPositionAnimator) to create? Or is there a more elegant way?
@MWB - There are several solutions depending on your overall design. Separate methods names is one good one. Another is to have AnimationStarter have a list of the kinds of animations it can start or similar.
I think what I will do is use the type of start or target. If this is a Paint, I need to create a PaintAnimator. If this is a 'Point', I need to create a PositionAnimator.
|
2

If you call ScreenElements createAnimator() method, which one is it supposed to use? That’s what the compiler is complaining about. You need to tell it what to do when that method is called. Based on the code I’m not sure. So you are correct that ScreenElement needs to implement the create animator method, that way the compiler knows what to do when that method is called.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.