Package org.cojen.maker


package org.cojen.maker
Dynamic Java class file generator. Here's a simple "hello, world" example:
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 of Object, 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, or FieldMaker — Specifies the type used by the given Variable or Field.
  • null — Specifies a context specific default such as void.class.
  • ClassDesc — Specifies a type descriptor.
When making a factory method that constructs the class being made, pass the current 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)
A 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 a Variable, a Field or a constant: Constants of type 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 to long
  • Boxing — Example: int to Integer
  • Widening and boxing — Example: int to Long, Number or Object
  • Reboxing and widening — Example: Integer to Long
  • Unboxing — Example: Integer to int (NullPointerException is possible)
  • Unboxing and widening — Example: Integer to long (NullPointerException is possible)
Automatic widening rules are stricter than what the Java language permits. In particular, 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);
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 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:
  • Interfaces
    Class
    Description
    Defines 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 a method.
    Allows new fields to be defined within a class.
    Represents a label bound to a method 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 a method.