1

My goal is this: Be able to save an array of objects (in this case; Animal) to disc. Then I would like to be able to load them. I have used JSON to do this, but left it out in this snippet for clarity. I would like to be able to make many more classes of Animal that will have many more fields (see Unicorn Class below).

The class of the object cannot be known in advance. How can I get addAnimal to work using a String input?

AnimalWrangler aw;

void setup()
{
  aw = new AnimalWrangler();

  aw.addAnimal("cat");
  aw.addAnimal("horse");
  aw.addAnimal("unicorn");
  aw.addAnimal("unknown");
}

class AnimalWrangler
{

  ArrayList<Animal> animals;

  AnimalWrangler()
  {
    animals = new ArrayList<Animal>();
  }

  void addAnimal(String type)
  {
    //makes an animal according to type        

    ///if type does not exist then throw

    //add to animals array 

  }
}



class Animal
{
  String type;

  Animal()
  {
  }
}

class Cat extends Animal
{
  Animal animal;
  Cat()
  {
    animal = new Animal();
    type = "cat";
  }
}

class Horse extends Animal
{
  Animal animal;
  Horse()
  {
    animal = new Animal();
    type = "horse";
  }
}

class Unicorn extends Animal
{
  Animal animal;
  float magic_level;
  /////classes can have any number of attributes

  Unicorn()
  {
    animal = new Animal();
    type = "unicorn";
  }
}

2 Answers2

1

The typical way is to create a registry of string-to-class mappings:

static Map<String, Class<? extends Animal>> registry;

Then populate the map with the types:

static {
            registry = new HashMap<>();
            registry.put("cat", Cat.class);
        }

Then, for AnimalWrangler.addAnimal("cat"); add to addAnimal method:

animals.add(registry.get("cat").newInstance());

Ought to solve it, so long as each class has a default constructor.

  • So when I save the animals I build the registry and save it to disc as well? – jackoverflow Jan 04 '17 at 13:30
  • to save it to disc you would use serialization. Make the top parent class implement `Serializable` then write each instance to a separate file. To read back: `registry.get("cat").cast(objectReadFromDeserialization); See how to ser/deser here http://www.javatpoint.com/serialization-in-java –  Jan 04 '17 at 13:37
  • This is a much less verbose solution, however there is some performance loss using reflection to generate a new Instance – CraigR8806 Jan 04 '17 at 13:42
0

Probably you're looking for a switch statement that does this for you, something like:

switch (type)
{
    type.equals("cat"): // Note hardcoding isn't the best idea and doesn't account for case sensitivity
        animals.add(new Cat());
        break;
    type.equals("horse"):
        animals.add(new Horse());
        break;
    // Add other animals in a similar fashion.
    default:
        throw new exception();
        break;
    }
}
VinKel
  • 121
  • 7
  • Is there an alternative to hardcoding? If I was a lazy programmer, how could I avoid writing out a new switch every time I make a new class? – jackoverflow Jan 04 '17 at 13:25
  • @jackoverflow The hardcoding you can solve with an enum, something like http://stackoverflow.com/questions/6667243/using-enum-values-as-string-literals. I wouldn't know how to avoid adding new animals to the switch. – VinKel Jan 04 '17 at 13:32