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 Object
s 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!