I'm currently writing a grid-based puzzle game, and have run into a problem.
I have an abstract Sprite class where each Sprite represents some object on my map (a player, a wall, etc). This Sprite class has children (Item, Entity, Obstacle), and these children have children (Item has InventoryItem and InstantUseItem), and so on. Only children without any further children are not abstract, so you can only instantiate concrete objects that you can find in-game (you can instantiate Sword and Arrow, but not Weapon - their parent object).
My problem is that I'm storing all the objects on Tiles (each map has width*height Tiles) in a Sprite ArrayList, and now I want to do something like find which Tile the Player object is on, or find all the Tiles that contain Enemy objects (or classes that inherit from them).
The problem is that, as far as I can tell, I can't do something akin to this in my Map or Tile object:
public ArrayList<t> findAllSpritesOfType(Type t) {
ArrayList<t> sprites = new ArrayList<t>();
for(Sprite s : this.getSprites()) {
if(s instanceof t) {
sprites.add((t) s); //Add after casting to right class
}
}
return sprites;
}
Even if I try to implement a static function in Sprite, I would need this (a kind of 'automatic covariance' amongst all of Sprite's children):
public static ArrayList<this.Type> getSpritesOnTile(Tile t) {
ArrayList<this.Type> sprites = new ArrayList<this.Type>();
for(Sprite s : t.getSprites()) {
if(s instanceof this.Type) {
sprites.add((this.Type) s); //Add after casting to right class
}
}
return sprites;
}
The other methods I have thought of involve:
Making sure my program conforms to Object Oriented Principles, and remains easily extensible, is very important to this project. Is there any way to implement any of my solutions, or achieve my goal?
It looks like this is what you want:
public <T extends Sprite> ArrayList<T> findAllSpritesOfType(Class<T> clazz) {
ArrayList<T> sprites = new ArrayList<>();
for(Sprite s : this.getSprites()) {
if(clazz.isInstance(s)) {
sprites.add((T) s);
}
}
return sprites;
}
And then you can use it like this:
List<Item> items = findAllSpritesOfType(Item.class);
Another way to do it would be to return a stream instead of a list:
public <T extends Sprite> Stream<T> findAllSpritesOfType(Class<T> clazz) {
return getSprites().stream().filter(clazz::isInstance).map(clazz::cast);
}