Creating Classes Dynamically with Java

Creating classes dynamically with Java

It is possible to generate classes (via cglib, asm, javassist, bcel), but you shouldn't do it that way. Why?

  • the code that's using the library should expect type Object and get all the fields using reflection - not a good idea
  • java is statically typed language, and you want to introduce dynamic typing - it's not the place.

If you simply want the data in an undefined format, then you can return it in an array, like Object[], or Map<String, Object> if you want them named, and get it from there - it will save you much trouble with unneeded class generation for the only purpose of containing some data that will be obtained by reflection.

What you can do instead is have predefined classes that will hold the data, and pass them as arguments to querying methods. For example:

 public <T> T executeQuery(Class<T> expectedResultClass, 
String someArg, Object.. otherArgs) {..}

Thus you can use reflection on the passed expectedResultClass to create a new object of that type and populate it with the result of the query.

That said, I think you could use something existing, like an ORM framework (Hibernate, EclipseLink), spring's JdbcTemplate, etc.

Create a dynamic class in java

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.

How to create a class dynamically in java

What would you do with a dynamically created and instantiated class that none of your other code knows about?

For a stically typed language like Java, it makes little sense to have such classes. On the other hand, most OR Mappers like Hibernate come with tools that allow you to statically generate classes from a database schema.

How to Create Java class/es files dynamically?

Working example which will help you.

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;

public class HelloWorld {

public static void main(String[] args) throws Exception {

// create an empty source file
File sourceFile = File.createTempFile("Hello", ".java");
sourceFile.deleteOnExit();

// generate the source code, using the source filename as the class name
String classname = sourceFile.getName().split("\\.")[0];
String sourceCode = "public class " + classname + "{ public void hello() { System.out.print(\"Hello world\");}}";

// write the source code into the source file
FileWriter writer = new FileWriter(sourceFile);
writer.write(sourceCode);
writer.close();

// compile the source file
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
File parentDirectory = sourceFile.getParentFile();
fileManager.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(parentDirectory));
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(Arrays.asList(sourceFile));
compiler.getTask(null, fileManager, null, null, null, compilationUnits).call();
fileManager.close();

// load the compiled class
URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { parentDirectory.toURI().toURL() });
Class<?> helloClass = classLoader.loadClass(classname);

// call a method on the loaded class
Method helloMethod = helloClass.getDeclaredMethod("hello");
helloMethod.invoke(helloClass.newInstance());
}
}

How can I dynamically create an instance of a class in java?

Just cast it to A:

instB = (A)Class....newInstance(...);

You don't need to know the exact class.

Automatically generate code to dynamically create Java objects (Reflection)

Using Reflection you can:

  1. Invoke static newBuilder method to create builder.
  2. Find all fields/setters for given POJO class. If in all cases setters on builder and setters on POJO have the same name like in your example it should be easy to convert.
  3. Invoke build on builder instance to create new POJO.

Using basic Java Reflection and Stream API-s it could look like below:

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Properties;
import java.util.StringJoiner;
import java.util.function.Function;
import java.util.stream.Stream;

public class ReflectionApp {
public static void main(String[] args) throws Exception {
Properties properties = new Properties();
properties.put("name", "John");
properties.put("date", "today");

AvroPropertie manualInstance = AvroPropertie.newBuilder()
.setName(properties.getProperty("name"))
.setDate(properties.getProperty("date"))
.build();

Object dynamicInstance = AvroAutoCoder.createAndSet(AvroPropertie.class, properties::getProperty);

System.out.println(manualInstance);
System.out.println(dynamicInstance);
System.out.println("manualInstance == dynamicInstance => " + manualInstance.equals(dynamicInstance));
}
}

class AvroAutoCoder {

public static Object createAndSet(Class clazz, Function<String, String> dataSupplier) throws Exception {
Object builderInstance = findMethod(clazz, "newBuilder")
.invoke(null);

Class<?> builderClass = builderInstance.getClass();
getSetters(clazz).forEach(setter -> {
try {
String fieldName = setter.getName().substring(3).toLowerCase();
findMethod(builderClass, setter.getName())
.invoke(builderInstance, dataSupplier.apply(fieldName));
} catch (Exception e) {
throw new IllegalStateException(e);
}
});

return findMethod(builderClass, "build")
.invoke(builderInstance);
}

private static Method findMethod(Class clazz, String methodName) {
return Arrays.stream(clazz.getDeclaredMethods())
.filter(method -> method.getName().equals(methodName))
.findFirst()
.orElseThrow(IllegalArgumentException::new);
}

private static Stream<Method> getSetters(Class clazz) {
return Arrays.stream(clazz.getDeclaredMethods())
.filter(method -> method.getParameterCount() == 1 && method.getReturnType() == Void.TYPE)
.filter(method -> method.getName().startsWith("set"));
}
}

Above code prints:

AvroPropertie[name='John', date='today']
AvroPropertie[name='John', date='today']
manualInstance == dynamicInstance => true

Comment added by the Author of question:

In this specific example class APropertie has a static inner class named Builder which holds the wanted setters. In this case the getSetters() methods needs to be changed a little bit:

// Find all setter of class
private static Stream<Method> getSetters2(Class clazz) {
Optional<Class> first = Arrays.stream(clazz.getDeclaredClasses())
.findFirst();

return Arrays.stream(first.get().getDeclaredMethods())
.filter(method -> method.getName().startsWith("set"));
}


Related Topics



Leave a reply



Submit