Class that creates another new superclass at runtime

Hi,

ok topic sucks and this is kinda hard to explain, but here we go:

In my game I have this workshop thing, which should be able to create items. For every item in game there is a different class, for example:

Item (Base class)
ItemDoor
ItemChair
ItemFence
ItemTree

Now, I have this list that my workshop holds, called buildableItems. It currently holds a string like “Build Door” (for the UI) and the information on what is required to build that thing.

Here is an example of the carpenter:


public class W_Carpenter extends Workshop {
    public W_Carpenter(AssetManager assets, Map map) {
        super(assets, "data/carpenter3.png", "data/carpenter0.png", "data/carpenter1.png", "data/carpenter2.png", map);
        MaterialsRequired r1 = new MaterialsRequired(ItemList.ITEM_LOG, 2);
        MaterialsRequired r2 = new MaterialsRequired(ItemList.ITEM_RAWSTONE, 1);
        requiredForConstruction.add(r1);
        requiredForConstruction.add(r2);
        name = "Carpenter";
        // ^^ this is only what's required to build the workshop

        BuildableItem itemDoor = new BuildableItem();
        itemDoor.name = "Build Door";
        MaterialsRequired b1 = new MaterialsRequired(ItemList.ITEM_LOG, 2);
        itemDoor.matsRequired.add(b1);
        buildableItems.add(itemDoor);

Now in that buildableItem object I want to hold the endProduct, which would be the class ItemDoor. How would I go about that?

Here’s buildableItem FYI


public class BuildableItem {
    public String name;
    public ArrayList<MaterialsRequired> matsRequired;
    // HERE I need something like
    // Item endProduct;

    public BuildableItem() {
        matsRequired = new ArrayList<MaterialsRequired>();
        name = null;
    }
}

But I can’t just go “Item endProduct” there, because then I would have to instantiate (?) the ItemDoor, which I don’t want to do (because it holds reference to the texture etc)…yet. If I don’t create the object I can’t even go through thousands of instanceof lines, because it would probably result in a null pointer exception I guess.

The endProduct (ItemDoor) should be created when all the requirements are fulfilled. So that the future code would look something like this


Item endProduct = new buildableItem.endProduct;

I hope I have explained that ok, any ideas on how to accomplish this? :smiley:

want you need is a item factory. You can use your BuildableItem class as a basis for it.
you would need to add a method which builds the item and some way to check if all the requiremnts are met.

You could implement this factory method in two ways, either subclass BuildableIteam for every Item class you have, our work with factory functions.

i.e.



public  abstract  class BuildableItem {
  abstract Item build();
}

//and then

BuildableItem bi = new BuildableItem(...){ Item build(){return new CoolItem();} }



Thanks for the help, but I don’t think I get this.

[quote]You could implement this factory method in two ways, either subclass BuildableIteam for every Item class you have,
[/quote]
I get that, and that’d probably be easy, but it’s not quite the solution I’m looking for.

[quote]our work with factory functions.
[/quote]
Yes please, but I don’t get it ->

Take a look at my class:


public class BuildableItem {
    public String name;
    public ArrayList<MaterialsRequired> matsRequired;

    public BuildableItem() {
        matsRequired = new ArrayList<MaterialsRequired>();
        name = null;
    }

    public Item buildItem() {
        // how do I know what kind of item to return here?
        // I need to store this information somehow in the class?
        // return new ItemDoor;
       // return new ItemChair;
    }
}

edit:
I could probably check the name and return the according item? But that’s a lot of
if( name.compareTo(“Door”) ) {
// bla
} else …

Not sure if that’s the right way

One way to get rid of the if/then/else checks is to use the Class object.

Some items:


public abstract class Item {
   public abstract String getName();
}

public class DoorItem extends Item {
   public String getName() {
      return "Door";
   }
}


public class  WallItem extends Item {
   public String getName() {
      return "Wall";
   }
}

Now create a BuildableItem that takes the class object you want it to create:


public class BuildableItem {

        // We use generics to make sure we only have classes that extend Item.
	private Class<? extends Item> itemClass;
	

	BuildableItem(Class<? extends Item> itemClass) {
		this.itemClass = itemClass;
	}
	

       // Here we are instantiating a class based on its Class Object.
      // NOTE: We are assuming these are all 0 argument constructors.
	public Item buildItem() {
		
		Item item = null;
		try {
			item =  itemClass.newInstance();
		} catch (Exception e) {
			e.printStackTrace();	
		}	
		return item;
	}
}

Now you can use the code like this:


   BuildableItem buildableDoor = new BuildableItem(DoorItem.class);

  Item item = buildableDoor.build();

  System.out.println(item.getName());  // <-- Will print out Door


EDIT: I read Danny’s post more carefully and I like his approach. It’s a bit more wordy on the usage end but you aren’t messing around with class objects.

Looking at your BuildableItem class, it would look like this:



// We don't create BuildableItems directly any more.
public abstract class BuildableItem {
    public String name;
    public ArrayList<MaterialsRequired> matsRequired;

    public BuildableItem() {
        matsRequired = new ArrayList<MaterialsRequired>();
        name = null;
    }

    public abstract Item buildItem();
}

Now, when you want a BuildableItem of a specific type, you create an anonymous class and override the buildItem method.



BuildableItem buildableDoor = new BuildableItem() {
      public Item buildItem() {
          return new ItemDoor();
      }
}


Item item = buildableDoor.buildItem();  //  <-- This will return an ItemDoor.

Thank you very much. Will try it tomorrow

Just to say, an imo cleaner, neater way to do this would be for BuildableItem to store an instance of the class it describes. Then when you need a new instance, class has a newInstance() method, or even better, a getConstructor() returning a specific constructor which can be called to return an instance.