Package org.cojen.maker
package org.cojen.maker
Dynamic Java class file generator. Here's a simple "hello, world" example:
A
Narrowing of primitive constants is performed automatically if no information would be lost
in the conversion. If the above example didn't have an explicit cast, passing the
ClassMaker cm = ClassMaker.begin().public_();
// public static void run()...
MethodMaker mm = cm.addMethod(null, "run").public_().static_();
// System.out.println(...
mm.var(System.class).field("out").invoke("println", "hello, world");
Class<?> clazz = cm.finish();
clazz.getMethod("run").invoke(null);
Types and Values
The API supports many kinds of data types and values. To keep things simple, types and values are passed as any kind ofObject
, but only a subset is allowed.
Types
The following kinds of types are supported:Type
— An explicit type object.Class
— Examples:int.class
,String.class
,int[].class
, etc.String
— Fully qualified class name or descriptor:"int"
,"java.lang.String"
,"int[]"
,"I"
,"Ljava/lang/String;"
,"[I"
, etc.ClassMaker
— Specifies the class being made.Variable
,Field
, orFieldMaker
— Specifies the type used by the givenVariable
orField
.null
— Specifies a context specific default such asvoid.class
.ClassDesc
— Specifies a type descriptor.
ClassMaker
. Unless explicitly specified, the actual name of the class being made isn't
known until it's finished.
ClassMaker cm = ...
MethodMaker factory = ...
// Pass the ClassMaker as the type to instantiate.
var instance = factory.new_(cm, ...);
...
factory.return_(instance)
Variable
can be used as a generic type carrier, and this won't actually allocate a variable slot.
MethodMaker mm = ...
var builderType = mm.var(StringBuilder.class);
var b1 = mm.new_(builderType, ...);
var b2 = mm.new_(builderType, ...);
...
Values
A value can be aVariable
, a Field
or a constant:
- Primitive constant — Examples:
123
,true
, etc. - Boxed constant —
Integer
,Boolean
, etc. String
constantClass
constantEnum
constantMethodType
constantMethodHandleInfo
constantConstantDesc
constantConstable
constant
MethodHandleInfo
are treated specially when assigning them to
variables or parameters of type MethodHandle
. A lookup is performed at runtime which
resolves the MethodHandle instance. Handling of ConstantDesc
and Constable
is also treated specially — the actual type is determined by the resolved constant.
Constants that aren't in the above set can be specified via Variable.setExact
or Variable.condy
. The setExact
method
supports any kind of object, but this feature only works for classes which are directly
finished
. If the class is written to a file and then loaded from
it, the constant won't be found, resulting in a linkage error.
Value type conversions
Automatic value type conversions are performed when setting variables or invoking methods:- Widening — Example:
int
tolong
- Boxing — Example:
int
toInteger
- Widening and boxing — Example:
int
toLong
,Number
orObject
- Reboxing and widening — Example:
Integer
toLong
- Unboxing — Example:
Integer
toint
(NullPointerException
is possible) - Unboxing and widening — Example:
Integer
tolong
(NullPointerException
is possible)
int
cannot be automatically widened to float
, and long
cannot be
automatically widened to double
. For these cases, an explicit cast is required.
In addition, a calculation on a small primitive type doesn't automatically get converted to
int
:
// Make an unsigned conversion: int a = bytes[i] & 0xff;
// Without the cast, the 'and' operation only accepts a byte, and the result would be a byte.
var aVar = bytesVar.aget(iVar).cast(int.class).and(0xff);
0xff
constant to an operation which accepts a byte
would cause an exception to be
thrown at code generation time. This is because bytes are signed, and 0xff
is out
of bounds. A constant of -1
would be accepted, although it wouldn't correctly
perform an unsigned conversion.
Thread safety
The classes which implement the interfaces in this package aren't designed to be
thread-safe. Only one thread at a time should be interacting with a ClassMaker
instance and any other objects that affect its state.
- See Also:
-
ClassDescriptionDefines the contents of an annotation.Represents an invoke dynamic bootstrap method which is bound to a
method
.Allows new classes and interfaces to be defined dynamically.Represents a field accessible by the body of amethod
.Allows new fields to be defined within a class.Represents a label bound to amethod
body.Base interface for making classes, methods, and fields.Allows new methods to be defined within a class.Describes a class which is being made, or describes an existing class or primitive type.Represents a variable bound to the body of amethod
.