0

I want to add two enemies to the pane, Dragon and Orc. Both of these classes extend a super class called Entity, and I want to create a single method for both these methods. I have tried using List<? extends Sprite> list and List<? super Sprite> list, but it didn't work as I need to both add the object to a list and read it later on when calling the add method.

public void addDragon(List<Dragon> list) {
    double imageWidth = 0;
    for(int i = 0; i < 6; i++) {
        Dragon dragon = new Dragon();
        imageWidth = dragon.getWidth();
        pane.getChildren().add(dragon);
        list.add(dragon);
    }
}

public void addDragon(List<Orc> list) {
    double imageWidth = 0;
    for(int i = 0; i < 6; i++) {
        Orc orc = new Orc();
        imageWidth = orc.getWidth();
        pane.getChildren().add(orc);
        list.add(orc);
    }
}
Yani
  • 11
  • 2
  • 5
  • "*I have tried using "List list" and "List list" [...]*" - I do not comprehend. A remark: instead of `List` and `List`, you should use `List super Dragon>` and `List super Orc>`, applying the [PECS mnemonic](https://stackoverflow.com/questions/2723397/what-is-pecs-producer-extends-consumer-super) – Turing85 May 03 '18 at 19:04
  • You could use an Interface. – Compass May 03 '18 at 19:05
  • Can you elaborate, Compass? :) – Yani May 03 '18 at 19:08
  • FYI, optimization generally refers to performance tuning. Merging redundant code is not an optimization in that sense. – shmosel May 03 '18 at 19:56

4 Answers4

2

Pass in a Supplier of the type you want to create:

public <T extends Sprite> void addThings(List<? super T> list, Supplier<T> supplier) {

And then replace the constructor with an invocation of that supplier:

T thing = supplier.get();
imageWidth = thing.getWidth();
pane.getChildren().add(thing);
list.add(thing);

You would invoke this something like:

addThings(listOfDragons, Dragon::new);
addThings(listOfOrcs, Orc::new);
Andy Turner
  • 137,514
  • 11
  • 162
  • 243
1

Considering your class Structure is like this

public interface Entity {
// abstract methods here
}

public class Orc implements Entity {
 //Code here
}


public class Dragon implements Entity {
 //Code here
}

now modify your method as below

    public <T extends Entity> void addDragon(List<T> list, Class<T> clazz) 
  throws IllegalAccessException, InstantiationException {
    double imageWidth = 0;
    for(int i = 0; i < 6; i++) {
        T enemy = clazz.newInstance();
        imageWidth = enemy.getWidth();
        pane.getChildren().add(enemy);
        list.add(enemy);
    }
   }

Here < T extends Entity > makes your method flexible to be called by passing any class which are implementing Enemies.

Kushagra Misra
  • 461
  • 1
  • 7
  • 15
  • This only works iff. `T` has ha parameterless, accessible constructor. – Turing85 May 03 '18 at 19:50
  • In which class do i place that particular method? Is public a new class? – Yani May 03 '18 at 19:50
  • @Yani no, it is a generic method. But from its declaration, the [solution of Andy Turner](https://stackoverflow.com/a/50162318/4216641) (using `List super T> list` as parameter) is cleaner. – Turing85 May 03 '18 at 19:53
0

Generic might help you

public <T extends Entity> void addEntity(List<T> list, Class<T> clazz) {
  double imageWidth = 0;
  for(int i = 0; i < 6; i++) {
    T entity = clazz.newInstance();
    imageWidth = entity.getWidth();
    pane.getChildren().add(entity);
    list.add(entity);
  }
}
Mạnh Quyết Nguyễn
  • 17,677
  • 1
  • 23
  • 51
0

Add an interface, or add methods to Entity, for the things you need to do to all entities. This would include a factory method to create a new instance of the specific subtype, and getWidth. Call addEnemy with a prototype instance of the type to be added.

public void addEnemy(List<Entity> list,Entity enemy) {
double imageWidth = 0;
for(int i = 0; i < 6; i++) {
    Entity someEnemy = enemy.createNewInstance();
    imageWidth = someEnemy .getWidth();
    pane.getChildren().add(someEnemy );
    list.add(someEnemy );
}

}

ddyer
  • 1,792
  • 19
  • 26