Generating Classes at Runtime and Invoking Their Methods With and Without the Use of Reflection in Java 8 and Later
In this article, take a look at generating classes at runtime and invoking their methods with and without the use of reflection in Java 8 and later.
Join the DZone community and get the full member experience.
Join For FreeThe generation of classes at runtime is an advanced topic that requires a lot of knowledge that can be reduced if you use particular libraries that perform the most complex functions to accomplish this task.
So, for this purpose, we can use the ClassFactory component and the sources generating components of the Burningwave Core library. Once the sources have been set in UnitSourceGenerator objects, they must be passed to loadOrBuildAndDefine method of ClassFactory with the ClassLoader where you want to define newly generated classes.
This method performs the following operations: tries to load all the classes present in the UnitSourceGenerator through the class loader, if at least one of these is not found it proceeds to compile all the UnitSourceGenerators and uploading their classes on class loader: in this case, keep in mind that if a class with the same name was previously loaded by the class loader, the compiled class will not be uploaded.
Once the classes have been compiled and loaded, it is possible to invoke their methods in several ways, as shown at the end of the example below.
package org.burningwave.core.examples.classfactory;
import static org.burningwave.core.assembler.StaticComponentContainer.Constructors;
import java.lang.reflect.Modifier;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import org.burningwave.core.Virtual;
import org.burningwave.core.assembler.ComponentContainer;
import org.burningwave.core.assembler.ComponentSupplier;
import org.burningwave.core.classes.AnnotationSourceGenerator;
import org.burningwave.core.classes.ClassFactory;
import org.burningwave.core.classes.ClassSourceGenerator;
import org.burningwave.core.classes.FunctionSourceGenerator;
import org.burningwave.core.classes.GenericSourceGenerator;
import org.burningwave.core.classes.TypeDeclarationSourceGenerator;
import org.burningwave.core.classes.UnitSourceGenerator;
import org.burningwave.core.classes.VariableSourceGenerator;
public class RuntimeClassExtender {
"resource") (
public static void execute() throws Throwable {
UnitSourceGenerator unitSG = UnitSourceGenerator.create("packagename").addClass(
ClassSourceGenerator.create(
TypeDeclarationSourceGenerator.create("MyExtendedClass")
).addModifier(
Modifier.PUBLIC
//generating new method that override MyInterface.convert(LocalDateTime)
).addMethod(
FunctionSourceGenerator.create("convert").setReturnType(
TypeDeclarationSourceGenerator.create(
Comparable.class
).addGeneric(
GenericSourceGenerator.create(Date.class)
)
)
.addParameter(VariableSourceGenerator.create(LocalDateTime.class, "localDateTime"))
.addModifier(Modifier.PUBLIC)
.addAnnotation(AnnotationSourceGenerator.create(Override.class))
.addBodyCodeRow(
"return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());"
).useType(ZoneId.class)
).addConcretizedType(
MyInterface.class
).expands(ToBeExtended.class)
);
System.out.println("\nGenerated code:\n" + unitSG.make());
//With this we store the generated source to a path
unitSG.storeToClassPath(System.getProperty("user.home") + "/Desktop/bw-tests");
ComponentSupplier componentSupplier = ComponentContainer.getInstance();
ClassFactory classFactory = componentSupplier.getClassFactory();
//this method compile all compilation units and upload the generated classes to default
//class loader declared with property "class-factory.default-class-loader" in
//burningwave.properties file (see "Overview and configuration").
//If you need to upload the class to another class loader use
//loadOrBuildAndDefine(LoadOrBuildAndDefineConfig) method
Class<?> generatedClass = classFactory.loadOrBuildAndDefine(
unitSG
).get(
"packagename.MyExtendedClass"
);
ToBeExtended generatedClassObject =
Constructors.newInstanceOf(generatedClass);
generatedClassObject.printSomeThing();
System.out.println(
((MyInterface)generatedClassObject).convert(LocalDateTime.now()).toString()
);
//You can also invoke methods by casting to Virtual (an interface offered by the
//library for faciliate use of runtime generated classes)
Virtual virtualObject = (Virtual)generatedClassObject;
//Invoke by using reflection
virtualObject.invoke("printSomeThing");
//Invoke by using MethodHandle
virtualObject.invokeDirect("printSomeThing");
System.out.println(
((Date)virtualObject.invokeDirect("convert", LocalDateTime.now())).toString()
);
}
public static class ToBeExtended {
public void printSomeThing() {
System.out.println("Called method printSomeThing");
}
}
public static interface MyInterface {
public Comparable<Date> convert(LocalDateTime localDateTime);
}
public static void main(String[] args) throws Throwable {
execute();
}
}
Opinions expressed by DZone contributors are their own.
Comments