Reputation: 3205
I have a set of classes that implements interfaces Paintable
and Tickable
.
Another class, World
, keeps track of some of these classes by shoving them into an ArrayList<Tickable>
.
I have a different class that works on all objects that are Paintable
, and a sure way to get all of these objects is to ask world for this with a getPaintables() method, as all objects held by World
are both tickable and paintable.
The probles arises from the from when I try to cast Tickable
objects as Paintable
ones like this:
for (Paintable p : (ArrayList<Paintable>)world.getPaintables()) {
// Do stuff
}
This results in:
error: incompatible types: ArrayList<Tickable> cannot be converted to ArrayList<Paintable>.
Is it possible to work around this by casting it some other way? I could, ofcourse duplicate the ArrayList
in World
, one as Paintable
, and the other one as Tickable
, but this seems cumbersome to me.
I hope that makes sense.
Upvotes: 1
Views: 62
Reputation: 7956
One alternative that doesn't require changing the class / interface hierarchies, is to declare the List
container in your World
class with a bounded type parameter having multiple bounds. For example:
class World<T extends Object & Paintable & Tickable> {
private List<T> tickablePaintables = new ArrayList<>();
/*...*/
public List<T> getTickablePaintables() {
return tickablePaintables;
}
/*...*/
}
Then in the client code, you could iterate through the tickable-paintable list returned by World
without casting:
public <T extends Object & Paintable & Tickable> void doSomething() {
World<T> w = new World<>();
for (Tickable tickable : w.getTickablePaintables()) {
//...
}
for (Paintable paintable : w.getTickablePaintables()) {
//...
}
}
Due to the way type parameters work in Java, this will unfortunately add some clutter to the class/method declarations (for declaring T
).
Upvotes: 1
Reputation: 1026
You don't need typecast at all. Implement Iterable<Paintable>
and Iterable<Tickable>
on World
and then you can simply do either for(Paintable p : world)
or for(Tickable t : world)
Both will work without typecasting.
Upvotes: 1
Reputation: 46
What datatype does return world.getPaintables()? If the return of that method is an ArrayList of Paintable, you should not cast anything. Anyway, you could do something like:
for(Object p : world.getPaintables()) {
if(p instanceOf Paintable){
(Paintable)p ... (do something);
}
}
Hope it helps you.
Upvotes: 0
Reputation: 11020
I think the easiest way to "fix" this is just to create a common interface and use that for all your lists. If all your classes implement both Tickable
and Paintable
as you say, you probably should be doing it this way anyway.
public class TickAndPaint
{
public static void main(String[] args) {
ArrayList<TickableAndPaintable> list = new ArrayList<>();
for( Paintable t : list ) {
}
for( Tickable t : list ) {
}
}
}
interface Paintable {}
interface Tickable {}
interface TickableAndPaintable extends Paintable, Tickable {}
Upvotes: 1
Reputation: 815
I don't get your question entirely, but I'll give it a try.
If world.getPaintables() returns List it's clear that your code does not work.
If world.getPaintables() holds both, the tickable and paintable objects one could do something like Louis said:
for (Object p : (ArrayList<Object>)world.getPaintables()) {
if (p instanceof Paintable)
paint();
else if (p instanceof Tickable)
tick();
}
Upvotes: 0