This discussion is archived
14 Replies Latest reply: Mar 21, 2010 4:54 AM by 800268 RSS

Interfaces and Abstract Classes

807580 Newbie
Currently Being Moderated
Hello everyone,
First of all, I'm sorry if this question belongs in another thread.

I want to paint images on an applet in a certain order- the painting proccess and the applet are not relevant to this problem, just to give you a basic idea what my aim is.

I have a class, PaintLayers, that holds an ArrayList of objects. Each one of these objects should have an integer named z which serves an as index for the painting order - lowest z is painted first.

Now, PaintLayers has a function for filling and removing from the ArrayList, and a function for drawing them. At the time of drawing, if the ArrayList is not sorted, it first sorts it.

In order to sort, though, each object in the ArrayList should have declared a compareTo(Object o) method, and this is where I'm not sure what to do. How should the objects in the list look?

The problem is that I want to add this functionallity to existing objects. That is, I already have classes that do other stuff, and I want for them to be able to be painted in the order PaintLayers chose.

So, abstract classes are probably not an option because some classes already extend library classes, say java.awt.Rectangle. The only other alternative I thought of is using interfaces, having a Paintable interface that extends compareTo, but then I can only write the header of the methods thus having to implement the same method in every class that has the interface.

The code I'm using at the moment:

The Paintable interface
import java.awt.Graphics2D;

public interface Paintable extends Comparable<Paintable> {
     public void paint(Graphics2D g2);
     public int getZ();
}
The PaintLayers paint method:
     
     public static void paint(Graphics2D g2) {
          if (!isSorted)
               Collections.sort(list);
          for (int i=0; i<list.size(); i++)
               list.get(i).paint(g2);
     }
A sample for a clas implementing the Paintable interface:
class Polygon implements Paintable{
     protected int z;
     
     //... (Rest of the class)

     public int getZ() {
          return this.z;
     }
     public int compareTo(Paintable obj) {
          if (getZ() == obj.getZ())
               return 0;
          else if (getZ() > obj.getZ())
               return 1;
          else
               return -1;
     }
     //...
}
With my current solution though, I'd have to copy the getZ and compareTo methods in every Paintable class, which seems wrong. Any ideas are welcome.
  • 1. Re: Interfaces and Abstract Classes
    807580 Newbie
    Currently Being Moderated
    Collections.sort() has another version which takes a comparator as a second argument. You could just use a comparator that can compare your superclass and supply that to this alternative version of sort().
  • 2. Re: Interfaces and Abstract Classes
    807580 Newbie
    Currently Being Moderated
    Wait... why aren't you just defining compareTo() for the superclass?
  • 3. Re: Interfaces and Abstract Classes
    807580 Newbie
    Currently Being Moderated
    Because I have no superclass. Polygon is just one of the classes that will implement the Paintable interface. Unless you're somehow talking about implementing it inside the Interface, which I don't know how to do since apparently I'm only allowed to define headers in there.

    Edit:

    I've tried to search for the other type of sort method, changed my code into this:

    The sort line:
    Collections.sort(list, new CompareZs<Paintable>());
    Added a new class for sorting:
    class CompareZs<T> implements java.util.Comparator<T> {
         public int compare(Object a, Object b) {
              Paintable a1 = (Paintable)a;
              Paintable b1 = (Paintable)b;
              
              if (a1.getZ() == b1.getZ())
                   return 0;
              else if (a1.getZ() > b1.getZ())
                   return 1;
              else
                   return -1;
         }
    }
    Also, I deleted the compareTo from Polygon, and removed the inheritance of Comparable from the Paintable interface. That's much better. Thanks!

    Any idea what can be done about the z integer though? I still have to define it and the getZ() method for every object that implements Paintable, and I imagine there's another way.

    Edited by: Yiftach on Mar 20, 2010 6:18 AM
  • 4. Re: Interfaces and Abstract Classes
    807580 Newbie
    Currently Being Moderated
    In situations like these I have used two solutions:

    1. A separate Map of image to integer that the painter keeps. Then you don't need to hold a z score in your objects
    2. Composition: Create a composite object that takes an image (or whatever) and a z score that is just mostly or wholly used inside your graphics package

    BTW, if you are looking to manipulate (drag, click, etc) your painter objects, you may want to have a look at tjacobs.ui.drag (for Components) and tjacobs.ui.shape (for Areas) in TUS
  • 5. Re: Interfaces and Abstract Classes
    807580 Newbie
    Currently Being Moderated
    An object has to hold a z value. It is not possible for the PaintLayers object to derive the order of painting in any way, it has to recieve the information from the object.

    Your second option sounds cumbersome, unless I misunderstood it. Are you suggesting creating a new class which holds a reference to the (currently) Paintable and a z value, which is sent to PaintLayers instead? Sounds like updating the z value would require communication between classes, and although it's possible it doesn't sound like the right way.
  • 6. Re: Interfaces and Abstract Classes
    807580 Newbie
    Currently Being Moderated
    Your second option sounds cumbersome, unless I misunderstood it.
    <sarcasm-off-just-barely>I've been doing this for a long time, presumably a lot longer than you have. Why would I suggest something to you that wasn't a good solution?</sarcasm-boiling-in-my-brain>
  • 7. Re: Interfaces and Abstract Classes
    807580 Newbie
    Currently Being Moderated
    I wasn't trying to offend, I'm sorry if you find the word "cumbersome" too harsh. The current problem though is that each class that wants to be able to be painted has to implement a one lime method though- and to me, that sounded more intuitive.

    Of course you wouldn't suggest something that wasn't a good solution if you thought it was good, but good is subjective- what you find good others might not like. There's no one way to solve it, and I was trying to many ways to bypass the problem.

    Looking for as many examples and solutions as possible shouldn't be surprising. I don't see why you have to flaunt your years of experience in anger just because I was trying to see other people's ideas before I use one. I may even use yours.
  • 8. Re: Interfaces and Abstract Classes
    807580 Newbie
    Currently Being Moderated
    Looking for as many examples and solutions as possible shouldn't be surprising.
    Good luck with that
    I don't see why you have to flaunt your years of experience in anger
    Obviously you are new to online forums as well. This is how it goes - wear a helmet!

    Honestly, I've gone thru being flamed on this and other forums much worse - don't take it personally, but learn from it
  • 9. Re: Interfaces and Abstract Classes
    807580 Newbie
    Currently Being Moderated
    tjacobs01 wrote:
    Looking for as many examples and solutions as possible shouldn't be surprising.
    Good luck with that
    I don't see why you have to flaunt your years of experience in anger
    Obviously you are new to online forums as well. This is how it goes - wear a helmet!

    Honestly, I've gone thru being flamed on this and other forums much worse - don't take it personally, but learn from it
    On the subject of a Paintable method, regrettably java doesn't support this in the core libraries. However what it does have is Paint. You might want to check out how I used it in tjacobs.ui.shape.AreaManager in [ TUS|http://tus.svn.sourceforge.net/viewvc/tus/tjacobs/] Paint has its pluses and minuses - minus is that there are only a couple of ways to get a Paint - Basically Color (which is a Paint) and using a BufferedImage. I tried creating a Paintable interface that would be a Paint but it's neigh on impossible. So the best way to handle this is to do your painting to a bufferedImage, create a Paint from that, and use Graphics2D to paint it.
  • 10. Re: Interfaces and Abstract Classes
    807580 Newbie
    Currently Being Moderated
    Yiftach wrote:
    Your second option sounds cumbersome, unless I misunderstood it.
    The idea is called a "wrapper" class, and it is so cumbersome that Java itself uses it in several of the core libraries. ;)
  • 11. Re: Interfaces and Abstract Classes
    807580 Newbie
    Currently Being Moderated
    First of all, thanks for taking the time to answer. I do appreciate it.

    I'm not looking for paint per se, just calling the paint methods of objects I have. These paint methods either draw a BufferedImage, draw an AWT shape or draw lines between points (the original polygon class I linked earlier). Painting doesn't give me any trouble, though. I'm just trying to make these methods be called in a certain order- they could be printing text to the console instead, for example.

    The wrapper solution as it was first presented involves having the new class have an integer and a BufferedImage, but not all of my objects' paint methods are for drawing a BufferedImage. Therefore, the paint method itself should be called.

    Now, if I understood correctly, the suggestion is to have every paintable class have a reference to an object that has a z value and a reference to the object that created it- and these new objects would be the ones sorted by their z values. Then, PaintLayers would call the wrappers referenced object's paint method at the time of painting. Is that correct?
  • 12. Re: Interfaces and Abstract Classes
    800268 Expert
    Currently Being Moderated
    The back reference is not needed. Just have Paintable which can be painted and then either a PaintPanel which has a list of Paintable sorted by z-order (implied by position in the list), or add one layer of indirection with a PaintLayer which has a z-order and a list of Paintable's and the PaintPanel paint the layers in z-order.

    Meaning the references only go one way: PaintPanel (-> PaintLayer*) -> Paintable*
  • 13. Re: Interfaces and Abstract Classes
    807580 Newbie
    Currently Being Moderated
    What are you calling a paintable? If a paintable is an object that implements the interface I currently use, then the first solution you descripe sounds like what I'm already doing. It still means you have to implement the "return z" method in every paintable.
    If you are calling the new ("wrapper?) object a paintable, you have to reference it if you plan to change your z values and update PaintLayers, and it has to reference you in order to call your paint method.

    Also, I'm trying to avoid having multiple objects with the same z, to fully control who's painted when, which makes the second solution identical to the first.

    (The usual: If I'm horribly wrong, please correct me).
  • 14. Re: Interfaces and Abstract Classes
    800268 Expert
    Currently Being Moderated
    No it means that the z-order is implied by it's relative location in the list. It has as drawback you cannot specify a specific z-order, only relative to other paintables. The PaintLayer would add that (or you could add in in PaintPanel)
    public class PaintPanel extends JPanel {
      // paintables ordered by painting/zOrder order
      private List<Paintable> paintables = ...;
    
      public void add(int index, Paintable p) {
        paintables.add(p);
      }
    
      @Override
      public void paintComponent(Graphics g) {
        for(Paintable p : paintables) {
          p.paint(g);
        }
      }
    }
    
    public class LayeredPaintPanel extends JPanel {
      private SortedMap<Integer, List<Paintable>> layers = ...;
    
      public void add(int zOrder, Paintable p) {
        List<Paintable> paintables = layers.get(zOrder);
        if(paintables == null) {
          paintables = ...
          layers.put(zOrder, paintables);
        }
        paintables.add(p);
      }
    
      @Override
      public void paintComponent(Graphics g) {
        for(List<Paintable> paintables : layers.values()) {
          for(Paintable p : paintables) {
            p.paint(g);
          }
        }
      }
    }