Invoking a common method among different object types

I am making an rpg game that has lots of different items. I want the different items to do different things when used, but I want them to all have the same methods so that I can call “hpPotion01.use()” on a healing potion and “bomb01.use()” on a bomb, for example. Although, the syntax that I want to use it in would be something like:

inventory[1].use();  //  This is just dummy code to illustrate the fact that I want an array of items.

To accomplish this I used an interface to establish the method “use()”, and I created a class for each item. So I have a “Bomb” class and a “HpPotion” class, and they have the same methods with different code inside the method.

Bomb bomb01 = new Bomb();
HpPotion hpPotion01 = new HpPotion();

interface Item {
  void use();
}

class Bomb implements Item {
  void use() {
    explode();
  }
}

class HpPotion implements Item {
  void use() {
    heal(20);
  }
}

The problem is when it comes to the player’s inventory. I want to have a list of these item objects and I want to be able to reference an index in the list and call the method “use()” from it.

ArrayList<Object> inventory = new ArrayList<Object>();

inventory.add(bomb01);

inventory.get(0).use();

How would I go about doing this? I tried making an ArrayList of generic Objects as you saw above, but I can’t invoke a specific method from a generic Object because I get the error “The function ‘use()’ does not exist”, so does anybody have a better way of doing this?

1 Like

ArrayList<Item> inventory = new ArrayList<Item>();

1 Like

I switched the Array type to the interface “Item,” and it seems I am allowed to call functions now. However, I am unable to directly access any variables. When I try to do:

inventory.get(0).name

I get the error “The global variable ‘name’ does not exist.” The only thing that I can think of to counteract this would be to create a function for each variable I have that returns the variable.

String getName() {
  return name;
}

Is there a more convenient way of accomplishing this, or is this just the way java works, since I know that interfaces don’t like instance variables.

The use of public attributes in Java (or any OO language) is not good programming practice hence protected or private attributes and the use of getters and setters as you did with name to access them.

One way round it would be to create an abstract class with the attribute and inherit from this class e.g.

abstract class Item {
  public Sting name;

  void use();
}

class Bomb extends Item {
  void use() {
    explode();
  }
}

class HpPotion extends Item {
  void use() {
    heal(20);
  }
}
1 Like

Using what I have learned so far. I re implemented the Item interface as an abstract class, and the syntax error does not show up anymore, however, this has introduced a new error during runtime. When I try to access the instance variable while the code is running, the program is halted with a “NullPointerException.” This is the code so far:

abstract class Item {
  public String name;

  void use() {};
}

class HpPotion extends Item {

  int hp;

  HpPotion(String _name, int _hp) {
    name = new String(_name);
    hp = _hp;
  }

  void use() {
    heal(hp);
  }
}

HpPotion hpPotion20;

ArrayList<Item> inventory;

void setup() {
  hpPotion20 = new HpPotion("Healing Potion", 20);

  inventory = new ArrayList<Item>;
  inventory.add(hpPotion20);
}

void draw() {
  text(inventory.get(0).name, 0, 0);  //  This right here is where the NullPointerException occurs.
}
import java.util.List;
final List<Item> inventory = new ArrayList<Item>();

void setup() {
  size(300, 200);

  fill(#FFFF00);
  textSize(050);
  textAlign(CENTER, CENTER);

  inventory.add(new HpPotion("Healing Potion", 20));
}

void draw() {
  final String name = inventory.get(0).toString();
  final int cx = width >> 1, cy = height >> 1;

  background(#0000FF);
  text(name, cx, cy);
}

abstract class Item {
  String name = getClass().getSimpleName();

  abstract void use();

  String toString() {
    return name;
  }
}

class HpPotion extends Item {
  int hp;

  HpPotion(final String label, final int health) {
    name = label;
    hp = health;
  }

  void use() {
    heal(hp);
  }

  void heal(final int hp) {
  }
}

The code you posted did not run for me so I am not sure how you got the NPE but change the two lines to
inventory = new ArrayList<Item>();
and
text(inventory.get(0).name, 0, 20);

change
name = new String(_name);
to this
name = _name;
shortcut for String objects :slight_smile:

1 Like

Right, I did make a few mistakes because I’m trying to simplify code that’s spread out among so many tabs, so those were just typos in the post. And the “new String(_name)” thing was just me trying different syntaxes to see if that would solve the NPE.

I’m going to try GoToLoop’s example and report back…

Well, I’m happy to say that GoToLoop’s method worked. Using the List Java library, the abstract class, and using a return function in the place of directly referencing a variable, that is my takeaway from this.

If you don’t mind me asking, what is the difference between the List library and an ArrayList?

Also, in order to access an item from the Inventory list, I have to use an integer as an index. I am fine with doing this, but I am just curious if it is possible to address the instance by it’s name? For example:

inventory.add(hpPotion20);  //  hpPotion20 being an instance of HpPotion.

The solution that first comes to mind is using an IntDict to translate the name “hpPotion20” to ‘0’. For example:

IntDict itemIndex = new IntDict();
itemIndex.set("hpPotion20", 0);

inventory.add(itemIndex.get("hpPotion20"));

This seems like the simplest solution, but I’m interested in knowing if there is a solution that involves less redundancy.

1 Like

List is the common interface for list-like (index-based) containers, such as the ubiquitous class ArrayList:
Docs.Oracle.com/en/java/javase/11/docs/api/java.base/java/util/List.html
Docs.Oracle.com/en/java/javase/11/docs/api/java.base/java/util/ArrayList.html

Much like Item itself is the common abstract class for all the other subclasses of your sketch.

Notice that in final List<Item> inventory = new ArrayList<Item>();, the field inventory is externally seen as of datatype List<Item>, but its true internal datatype is ArrayList<Item>.

Given List is an index-based container, you’re gonna need a diff. type of container like a Map, which is dictionary-based:
Docs.Oracle.com/en/java/javase/11/docs/api/java.base/java/util/Map.html

The most famous dict-like container in Java is the class HashMap:
Docs.Oracle.com/en/java/javase/11/docs/api/java.base/java/util/HashMap.html
Processing.org/reference/HashMap.html

import java.util.Map;
final Map<String,Item> inventory = new HashMap<String,Item>();
2 Likes

As always, thank you, GoToLoop, for the amazing explanations and examples, and I will use this information to expedite my coding endeavors. I commend you for going out of your way to explain Java concepts to those of us who are not as experienced.

1 Like