0

I'm making a small RPG. There is an Item class which is the parent of each item in the game. These items could be Potion (which is a class) or Bandage (which is a class).

The Item class looks like this:

public class Item
{
int qty;
String name;
Hero hero1;

public void passHero(Hero hero1)
{
    this.hero1 = hero1;
}

public void use()
{
    if(qty == 0)
    {
        System.out.println("You have no more of this item to use.");
    }
    else
    {    
        qty--;
    }
}

public void addInv(int value)
{
    qty = qty + value;
}
}

A method for passing in the Hero class. A method for using an item. A method for adding to the inventory of the item.

This method activates these item classes:

public void initializeItemInventory()
{
    items[0] = new Potion();
    items[1] = new Bandage();
}

And this method would theoretically print all the items and their quantities:

public void useInventory()
{
    for(int i = 0; i<items.length; i++)
    {
        System.out.println("Enter: " + i + " for " + items[i].name);

    }

    int response = input.nextInt();
    items[response].use();

}

The Potion class, as an example, has an instance variable like:

String name = "Potion";

So my question. Why isn't the name variable from Potion being called correctly in the useInventory method. It returns null which tells me it's returning the parent class Item name, and not the name of the individual subclass variables.

Joseph Erickson
  • 938
  • 3
  • 8
  • 24
  • 3
    possible duplicate of [Java inheritance overriding instance variable](http://stackoverflow.com/questions/14933555/java-inheritance-overriding-instance-variable) – aruisdante Feb 17 '15 at 19:27
  • @aruisdante suggestions? – Joseph Erickson Feb 17 '15 at 19:28
  • 1
    Side note on your design: do you think that an item *owns* a hero? – m0skit0 Feb 17 '15 at 19:29
  • 1
    Read the duplicate question and its answers. And the other question and its answers that the duplicate is a duplicate of. If you're still unclear, add additional details to your question as to what in those duplicates don't answer your question. – aruisdante Feb 17 '15 at 19:29
  • Well, the first question that came to my mind is: why does the `Item` care about `Hero`? A hero might "know" his inventory, but the Item shouldn't care about his carrier. – Tom Feb 17 '15 at 19:30
  • @Tom Actually it makes more sense that way to some degree; a hero shouldn't need to know how to use items, items should know how to act on a hero. That doesn't seem to be the design as current, but it actually is a pretty sensical OO design choice. Certainly it could (probably should) be implemented as `useMe(hero)` rather than storing a hero reference however. Not really relevant to the OP's question though. – aruisdante Feb 17 '15 at 19:33
  • @aruisdante A `#use` method *might* need a hero to act on, but then it should passed only in that method (e.g. "throwing" items to react on a different hero). Binding the item to a hero is still unnecessary. `Not really relevant to the OP's question however.` No one asserted that. – Tom Feb 17 '15 at 19:37
  • possible duplicate of [Java Inheritance - instance variables overriding](http://stackoverflow.com/questions/2464222/java-inheritance-instance-variables-overriding) – eckes Feb 18 '15 at 01:20
  • @aruisdante I've been thinking a lot what you said, of `Item` not owning `Hero` which is fair. Keep in mind im new to programming and Java is my first language. How else can a variable like, say, the hero's HP be obtained from another class, say, the `Item` class without passing `Hero` into `Item`. The method needs to occur within `Item`, not inside the main line. Does that make sense? It's just it feels like passing Hero to Item is the only way to go. – Joseph Erickson Feb 20 '15 at 23:15
  • It shouldn't access the health of Hero directly; Instead, `Hero` should provide a way to add a buff/debuff to a stat (say, `Hero.modifyHealth(adjustment)`). The `Item` class would then call this method and provide it with the amount to adjust health by. `Hero` then does whatever is needed to actually implement that modification. Think of the interaction as a 'conversation' between the objects; The Hero says "hey Item, I want to use you on me", to which the Item replies "Ok Hero, you should modify this stat by this amount". – aruisdante Feb 21 '15 at 01:31
  • Very good. Makes sense. – Joseph Erickson Feb 21 '15 at 02:21
  • Although...whats the downside of referring directly to a local variable of an instance as opposed to using a method to refer to it: hero1.heroHP or hero1.getHP(); why is the latter preferred? – Joseph Erickson Feb 21 '15 at 02:27

2 Answers2

3
public class Item
{
  int qty;
  String name;
  ...

The Item class already has name, and that's what you access from an Item-typed variable:

items[0].name

So if you have

public class Potion extends Item
{
  String name = "Potion";
  ...

then the Potion class has two name fields:

Potion p = new Potion();
System.out.println(p.name);
System.out.println((Item) p).name);

As you say, you want polymorphism, but it only applies to methods. Therefore you need a getter:

public class Item
{
  String name;
  public String getName() { return name; }
  ...

In the Potion subclass you may have

public class Potion extends Item
{
  public Potion() { this.name = "Potion"; }
  ...

and items[0].getName() will now work as expected.

Additional note

I'll add this to show a bit of the power of polymorphism.

If you happened to have the name property always the same for all the instances of the same class, you could easily refactor your getter-based solution by completely eliminating the need to store a name variable:

public class Item
{
  public String getName() { return "Generic item"; }
  ...

public class Potion extends Item
{
  @Override public String getName() { return "Potion"; }
  ...
Community
  • 1
  • 1
Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
2

Instead of declaring a new variable in your subclass like "String name = "Potion";" Use your constructor to pass the value to your superclass, something like this:

// the Item supuerclass has one constructor
public Item(name) {
  this.name = name;
} 

// the Potion subclass has one constructor
public Potion() {
  super("Potion");
}
Flyaway
  • 69
  • 4
  • The downside to this approach is that you now have a duplicate `name` string in every `Item` class instance, even though they all point to the same object. This would make sense if `name` was relatively unique per item. If they're basically just a duplication of the class name however (as in the OP's design), it would more efficient to just have a single static name shared by all instances of the class. – aruisdante Feb 17 '15 at 19:40