Home > other >  Unable to reference object attributes
Unable to reference object attributes

Time:10-31

First question on Stack Overflow! I feel like this has to be a common question amongst Java beginners. But I've been trying for hours and have been unable to discover the solution. I thought that object attributes could be accessed this way.

At first I thought that weapon[0] was, in fact, an object array, so when I created object array Inventory[] inventory, I was using an object array in the constructor. I immediately fixed that, but this issue still persists.

It's even more annoying because, in debug mode, I can literally see weapon[0] inside of Inventory[] inventory with its attributes. Look at Eclipse mock me.

My current theory is that placing object weapon[0], instance of class Weapons, in the object array Inventory[] inventory may be the issue, and that the object's attributes aren't accessible somehow because of that placement. Any help would be appreciated, thanks! And this is my first time messing with arrays, so I am definitely a novice. Any tips about my formatting and such would also be very helpful!

package arraytest;

import java.util.Scanner;
import java.util.InputMismatchException;
import java.lang.NumberFormatException;

public class ArrayTest {
    
    static Scanner kb = new Scanner(System.in);
    
    static int i = 0;
    static int choice = 0;
    
    public static void main(String[] args) {
        
        Weapons[] weapon = new Weapons[3];
        weapon[0] = new Weapons(0,"Wooden Sword",1,2);
        weapon[1] = new Weapons(1,"Bronze Sword",2.5,7.5);
        weapon[2] = new Weapons(2,"Iron Sword",5,10);
        
        Armor[] armor = new Armor[3];
        armor[0] = new Armor(3,"Wooden Armor",2,5);
        armor[1] = new Armor(4,"Bronze Armor",3,10);
        armor[2] = new Armor(5,"Iron Armor",5,15);
        
        Enemy[] enemy = new Enemy[3];
        enemy[0] = new Enemy(0,"Skeleton",3,0,10);
        enemy[1] = new Enemy(1,"Goblin",2,1,5);
        enemy[2] = new Enemy(2,"Zombie",4,1,8);
        
        Inventory[] inventory = new Inventory[256];
        
        String chooseweapon = String.format(
                "Choose your weapon:\n"
                  "1. %s\n"
                  "2. %s\n"
                  "3. %s\n"
                ,
                weapon[0].name,
                weapon[1].name,
                weapon[2].name
                );
        System.out.print(chooseweapon);
        
        while (i==0) {
            i  ; //1
            
            try {
                choice = Integer.parseInt(kb.nextLine());
            } catch (NumberFormatException e) {
                System.out.println("Error. Try again.");
                i--; //0
                continue;
            }
            if (choice < 1 || choice > 3) {
                System.out.println("Error. Try again.");
                i--; //0
            }
        }
        if (choice == 1) {
            inventory[0] = new Inventory(weapon[0]);
        }
        System.out.println(inventory[0].item); //this is the problem here. i can't put .item.name, error is "name cannot be resolved or is not a field"
    }
}



class Inventory {
    
    public Object item;
    Inventory(Object item) {
        this.item = item;
        
    }
}

class Armor {
    public int id;
    public String name;
    public double defnum;
    public double val;
    
    Armor(int id, String name, double defnum, double val) {
        this.id = id;
        this.name = name;
        this.defnum = defnum;
        this.val = val;
    }
}

class Weapons {
    public int id;
    public String name;
    public double attdmg;
    public double val;
    
    Weapons(int id, String name, double attdmg, double val) {
        this.id = id;
        this.name = name;
        this.attdmg = attdmg;
        this.val = val;
    }
}

class Enemy {
    public int id;
    public String name;
    public double attdmg;
    public double defnum;
    public double health;
    
    Enemy(int id, String name, double attdmg, double defnum, double health) {
        this.id = id;
        this.name = name;
        this.attdmg = attdmg;
        this.defnum = defnum;
        this.health = health;
    }
}

CodePudding user response:

Welcome to StackOverflow! This is a well-worded question!

This has to do with the difference between the types at runtime vs. the types at compile time. You declared item to be of type Object.

Java allows for polymorphism, which, when you declare the item in Inventory to be type Object, allows you to assign anything to item which "is-a" Object (so that means you can assign a String, an Integer, any object to item, since those all inherit from the Object class).

However, when you go to access item later on in the program, Java can't guarantee at compile-time that whatever item references has a name property! Integer, for example, is an Object, but does not have a name property! The Java compiler just says, "all I know is that item is an Object, and I won't let you access a property not all Objects have!".

When you run the program, however, the runtime-type of item is Weapon, so Eclipse is able to show you its properties. But Java is designed to catch as many errors as possible at compile-time, so it will not allow you to access the name property if it can't guarantee at compile-time that it has a name.

This may seem annoying or unnecessarily restrictive (you know everything you are going to put in Inventory will have a name!), so this is the point of superclasses & interfaces! These features allow you the flexibility to create different types of objects that all share similar properties or methods, and still allows Java to catch all these potential issues upfront.

To fix this, you can create an InventoryItem superclass that both Armor and Weapon extend, that has a name property. You can then declare item to be of type InventoryItem. That way, Java will know that, even though the runtime-type may be either a Weapon or Armor, it's guaranteed to have a name.

We can introduce a new class, such as InventoryItem:

class Inventory {

    public InventoryItem item;
    public int id;

    Inventory(InventoryItem item, int id) {
        this.item = item;
        this.id = id;
    }
}

And then class Inventory can take an InventoryItem (I suggest probably the inventory to contain the array of items)

class Inventory {

    public InventoryItem item;

    Inventory(InventoryItem item) {
        this.item = item;
    }

And then your classes, such as Armor, can extend InventoryItem, for example:

class Armor extends InventoryItem {

    public double defnum;
    public double val;

    Armor(int id, String name, double defnum, double val) {
        super(name, id);
        this.defnum = defnum;
        this.val = val;
    }
}

And then this will work!

System.out.println(inventory[0].item.name); //Java now knows that inventory[0] is type Inventory, its "item" property is type InventoryItem, and that is *guaranteed* to have a name and id!
  • Related