user_01
user_01

Reputation: 457

Create a dynamic class in java

I'm working on a problem where different animal types implement the same talk() method from Animal interface.
If you look at getAnimal() method, you can see that, when a new kind of animal is added to the program, inside of that method has to be changed as well.
I want to add new animals just by subclassing Animal without changing anything in the already existing classes.
For example, add an animal "Dog", criteria="loyal"; talk="woof".
Could you tell me, how it is possible? Below is my code:

interface Animal {

    public void talk();
}

class Lion implements Animal {

    @Override
    public void talk() {
        System.out.println("ROARRRRR");
    }
}

class Mouse implements Animal {

    @Override
    public void talk() {
        System.out.println("SQUEEEEEAK");
    }
}

class Bison implements Animal {

    @Override
    public void talk() {
        System.out.println("BELLOWWWWW");
    }
}

class AnimalType {

    public static Animal getAnimal(String criteria) {

        // I refactor this method
        if (criteria.equals("small")) {
            return new Mouse();
        } else if (criteria.equals("big")) {
            return new Bison();
        } else if (criteria.equals("lazy")) {
            return new Lion();
        }
        return null;
    }
}

public class AnimalExamples {

    public static void main(String[] args) {
        AnimalType.getAnimal("small").talk();
        AnimalType.getAnimal("big").talk();
        AnimalType.getAnimal("lazy").talk();

        //  how to add an animal "Dog" here, criteria="loyal"; talk="woof"
        AnimalType.getAnimal("loyal").talk();

        try {
            AnimalType.getAnimal("small").talk();
        } catch (Exception ex) {
            System.out.println("Animal does not exists");
        }
    }
}

I searched on google, understood it can be done by reflection. But do not know how. If possible, could you help me with this, please? Thanks in advance!

Upvotes: 3

Views: 17040

Answers (4)

Hrabosch
Hrabosch

Reputation: 1583

Old question, but here is how to create class... For me the easy way is to use Javassist. I created a small example here: http://hrabosch.com/2018/04/08/generate-class-during-runtime-with-javassist/

But here is main point:

public static Class generateClass(String className, String methodName, String methodBody)
  throws CannotCompileException {
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass(className);
StringBuffer method = new StringBuffer();
method.append("public void ")
      .append(methodName)
      .append("() {")
      .append(methodBody)
      .append(";}");
cc.addMethod(CtMethod.make(method.toString(), cc));
return cc.toClass();
}

So what I did... Via Javassist I made a class in ClassPool. Also I added a method inside this class and via reflection I invoked it.

Hope it helps.

Just keep on mind whatever you want to use in generated class, there are NOT imports, so you have to use fully-qualified names.

Upvotes: 3

sprinter
sprinter

Reputation: 27946

Java doesn't support creating a class at runtime. However there are really better ways of achieving what you want here. I'll propose two.

Firstly, you could create an AnimalType class that contains all the shared behaviour about a species. You could then have an Animal class that takes an AnimalType as a constructor parameter.

Secondly, you could use a prototype design pattern. In this case the Animal class would need a clone method to create a new animal from the prototype. The factory class could then have a list of the prototypes and use whatever logic you desire to choose the correct prototype to clone.

Comment below if you want further details or sample code for either of these options.

Upvotes: 1

Display Name
Display Name

Reputation: 952

Just so you know runtime class generation is extremely complex and not something recommended for beginners to the language. This would be an excellent scenario to use a map an anonymous classes.

class AnimalType {
    private static final Map<String, Animal> animals  = new HashMap<String, Animal>();

    static {
        // Populating map with default animals
        addAnimal("big","BELLOWWWWW"); // bison
        addAnimal("small","SQUEEEEEAK"); // mouse
        addAnimal("lazy","ROARRRRR"); // lion
        addAnimal("loyal","WOOF "); // dog
    }

    public static void addAnimal(String criteria, final String sound) {
        // Assigning a anonymous implementation of animal to the given criteria
        animals.put(criteria, new Animal() {
            @Override
            public void talk() {
                System.out.println(sound);
            }
        });
    }

    public static Animal getAnimal(String criteria) {
        // Returning an animal from the animals map
        return animals.get(criteria);
    }
}

If you really do insist on true runtime class generation or if you're curious how it works, check out ByteBuddy.

Upvotes: 5

you have to define the dog class

class Dog implements Animal {

    @Override
    public void talk() {
        System.out.println("woof");
    }
}

and add the if else to AnimalType

} else if ("loyal".equals(criteria)) {
      return new Dog();
}

Upvotes: 0

Related Questions