Java Swing's equivalent for WPF's Data Templates

Viewed 127

In a Swing application, I'm drawing elements on a JPanel overriding the paintComponent method.

I have to draw cats, dogs, camels, and more... I guess that polluting paintComponent with a series of if (... instanceof ...) statements would be a bad thing, so I'm looking for another way to draw my pets.

With WPF ( C# ), I'd have used Data Templates, i.e. a way to tell the View how to display a Model entity.

So, I came up with a solution ported from JS:

import my.super.Callback; // Because Runnable does not take parameters.

/**
 * The class that draws in the canvas.
 */
public class CanvasDrawer {
    private HashMap<Class, Callback<Graphics2D, Object>> drawers;
    private Graphics2D graphics;

    public CanvasDrawer(Graphics2D g) {
        graphics = g;
        drawers = new HashMap<Class, Callback<Graphics2D, Object>>();
    }

    /**
     * Defines a way to draw the objects of a certain type in the canvas.
     */
    public <T> void setDataTemplate(Class<T> type, Callback<Graphics2D, T> drawer) {
        drawers.put(type, (Callback<Graphics2D, Object>)drawer);
    }

    /**
     * Actually draws an object in the canvas.
     */
    public void draw(Object object) {
        drawers.get(object.getClass()).run(graphics, object);
    }
}

... Which could be used like this:

class HuntingPartyView extends JPanel {
    CanvasDrawer drawer;

    public HuntingPartyView() {
        drawer = new CanvasDrawer((Graphics2D)getGraphics());

        // Where the drawImage method is intentionally skipped in this code.
        drawer.setDataTemplate(   Cat.class, (g, o) -> drawImage(g, 'res/cat.png',    o.x, o.y));
        drawer.setDataTemplate(   Dog.class, (g, o) -> drawImage(g, 'res/dog.png',    o.x, o.y));
        drawer.setDataTemplate(Sniper.class, (g, o) -> drawImage(g, 'res/sniper.png', o.x, o.y));
        drawer.setDataTemplate(Ranger.class, (g, o) -> drawImage(g, 'res/ranger.png', o.x, o.y));
    }

    @Override
    public void repaintComponent(Graphics g) {
        // Where Game is part of the Model layer.
        for (Animal animal : Game.singleton.animalList) drawer.draw(animal);
        for (Hunter hunter : Game.singleton.hunterList) drawer.draw(hunter);
    }
}

... But the code of the CanvasDrawer above gives me an unchecked warning when I compile it:

$ javac *.java -d out -Xdiags:verbose -Xlint:unchecked
CanvasDrawer.java:15: warning: [unchecked] unchecked cast
                drawers.put(type, (Callback<Graphics2D, Object>)drawer);
                                                                ^
  required: Callback<Graphics2D,Object>
  found:    Callback<Graphics2D,T>
  where T is a type-variable:
    T extends Object declared in method <T>setDataTemplate(Class<T>,Callback<Graphics2D,T>)
1 warning

Question 1. Did I miss something during my research? I mean: do Data Templates exist in Java Swing?
Question 2. Is there another way to do what I want to do? Am I going the right way?
Question 3. Do I really have to use @SuppressWarnings("unchecked")?

2 Answers
Related