Stack Overflow is a community of 4.7 million programmers, just like you, helping each other.

Join them; it only takes a minute:

Sign up
Join the Stack Overflow community to:
  1. Ask programming questions
  2. Answer and help your peers
  3. Get recognized for your expertise

Let's say I have a class Text which implement an interface called Drawable which is defined as follow :

public interface Drawable {

  public void draw (DrawConfig drawConfig);
}

I want to have an object which acts like an array with items but which implements Drawable as well, to be able to write drawables.draw(drawConfig) and having it forward the call to all its children instead of having to do a for loop. So I first created a wrapper class which looks like this :

public class Drawables implements Drawable {

  private final Array<Drawable> items = new Array<Drawable>();

  public void add (final Drawable value) {
    this.items.add(value);
  }

  @Override
  public void draw (final DrawConfig drawConfig) {
    for (final T item : this.items)
      item.draw(drawConfig);
  }
}

The first problem is that as it's a wrapper it doesn't have any of the Array class methods and I have to manually add the ones I need (like add() in the example above).

The second problem is that if I store only Text objects in it, I can't write :

for (Text t : drawables)
    t.setText("blablabla");

Which means I have to make another array and I'll end up with two arrays just for that. I want to avoid that so I thought about using genericity as the Array class is generic and I turned my Drawables class into this :

public class Drawables<T extends Drawable> extends Array<T> implements Drawable {

  @Override
  public void draw (final DrawConfig drawConfig) {
    for (final T item : this.items) // Line 17
      item.draw(drawConfig);
  }
}

And now I can write :

Drawables<Text> drawables = new Drawables<Text>();

drawables.add(new Text());
drawables.add(new Text());

for (Text t : drawables)
    t.setText("blablabla");

drawables.draw(drawConfig);

And it compiles !! Except that at runtime I get the following error :

java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [La.b.c.ui.Drawable;
    at a.b.c.Drawables.draw(Drawables.java:17)
    at a.b.c.ui.components.Text_UseTest.render(Text_UseTest.java:80)
    at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:215)
    at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:120)

See the comment in my code for line 17.

What is the problem? And is it even possible to achieve what I want to do?

share|improve this question
    
What is Array<T>? It's not the built-in Array class. – markspace Jul 7 '15 at 20:11
1  
    
@LucasRoss, but it can behave like one. That is all an interface states – WorldSEnder Jul 7 '15 at 20:17
    
I'm not 100% sure how your custom Array<T> class works, but there's some information in this StackOverflow question about dealing with arrays and generics: stackoverflow.com/questions/30388464/… – markspace Jul 7 '15 at 20:22
up vote 1 down vote accepted

The class Array implements Iterable. So you can write like this.

@Override
public void draw (final DrawConfig drawConfig) {
  for (final T item : this) // Line 17
      item.draw(drawConfig);
}
share|improve this answer
    
IT WORKS ! Thanks :) – Flawyte Jul 7 '15 at 20:42

I don't think you have your design right. Instead of separating drawing a single item and drawing multiple items, Drawable should only specify in its contract that it draws according to some config. How it does this, or what other objects it uses to do this, is not important.

Hence, I would first do this:

public interface Drawable {
    void draw (DrawConfig drawConfig);    
}

Now it appears that you want your Drawable to maintain a private member. This private member has some bearing on how things are drawn. The interesting bit is that it could be the thing itself being drawn, or perhaps it could contain things that need to be drawn, or maybe it's something else entirely. We don't know at this point how it would impact the behavior of draw. So how do we handle that? Well we can create an abstract class that maintains this private member and also implements Drawable:

public abstract class AbstractDrawable<T> implements Drawable {    
    protected T data;           
}

Notice that there is no implementation for draw. This is on purpose because it's only in the concrete implementation that we will actually define a behavior for draw. You can easily see now how we have split two concerns (maintaining state and implementing behavior). Now we can define a concrete implementation which define how it will maintain state and how it will implement behavior:

public class Drawables<T extends Drawable> extends AbstractDrawable<List<T>> {

    public Drawables() {
        this.data = new ArrayList<T>();
    }

    public void addDrawable(T item) {
        this.data.add(item);
    }

    @Override
    public void draw (final DrawConfig drawConfig) {
        for (final T item : this.data) {
            item.draw(drawConfig);
        }
    }
}

Here we have a very specific implementation of AbstractDrawable<T> that itself works with a list of drawables. This concrete implementation has defined how it manages state (it uses an internal list of Drawable instances) and how it manages behavior as well (its "draw" behavior is to "draw" each Drawable instance that it maintains).

You mentioned that you want all the list methods, etc. It's not a good idea to extend a class simply to get access to its method. Your list of drawable items is not really a list. It is a drawable that happens to work with a list of drawables. The approach above implicitly uses composition over inheritance, because AbstractDrawable<T> encapsulates some internal data that will be drawn in some manner (not important how this is done; just that it is done). So instead of extending List<T>, you can now maintain an internal list that you can then "draw" in a manner you choose. This is exactly what happens in the concrete implementation Drawables<T extends Drawable>.

Your next question is about not being able to call t.setText() on Text instances. The only way you can do this is if you have a concrete instance that works specifically on Text instances, or if you performed instanceof checks inside draw. This is because all you have is T extends Drawable; the class knows is that it is some kind of Drawable, and that it is able to draw. Other than that, it has no idea about the specific implementations.

share|improve this answer
    
For this to work AbstractDrawable has to be declared abstract and data has to be declared protected. Also, I tried your code and I can't instanciate the Drawables class cause whetever I pass to it for the type T I have an error. – Flawyte Jul 7 '15 at 20:44
    
@Flawyte Edited the typos. You'll never instantiate a Drawable directly. I'm assuming you have stuff like Text which implements Drawable. So what you're going to be creating is an instance of Drawables<Text>. You could create an instance of Drawables<Drawable> as well, and give it anything that implements Drawable. – Vivin Paliath Jul 7 '15 at 20:48
    
Yeah with your post edited now I can do Drawables<Text> drawables = new Drawables<Text>() but I have two problems : 1) I still have to manually add add(), remove() methods etc. to AbstractDrawable and 2) I can't loop over my drawables object anymore cause it's not Iterable. – Flawyte Jul 7 '15 at 20:52
    
I don't think those are problems. You have to think about how you're using your Drawables object. Do you care more about the things inside it being drawn, or do you care about having access to those things themselves? It's not too much work to include add and remove, and then delegating to the internal list. If you really want access to the list of drawables, you could add a method that returns the internal instance. But I don't see why you couldn't maintain an external list and then just supply elements to Drawables. Try not to assign too many responsibilities to one class (SRP). – Vivin Paliath Jul 7 '15 at 21:03

The problem after erasure:

for (final Drawable item : this.items) // this.items is of Object[]!!!

Now the for loop doesn't know how to cast (no implicit casts in java). You couldn't simple write Drawable item = new Object();. You have to do the cast yourself:

for (Object o : this.items) {
  Drawable item = (Drawable) o;
  item.draw(drawConfig);
}

Related: Cast element in Java For Each statement

share|improve this answer
    
Wouldn't casting each item be very expensive? And as I'm extending Array<T> I don't understand why it's considered as an array of Object and not as an array of Text when I instanciate it. – Flawyte Jul 7 '15 at 20:21
    
@Flawyte, check this question if you want to understand erasure and why it is needed. – WorldSEnder Jul 7 '15 at 20:23

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.