ReflectionUtil class provides comprehensive utilities for Java reflection, including class loading, method invocation, field access, and NMS/OBC operations.
Class operations
Load classes
// Load class by full path
Class<?> clazz = ReflectionUtil.lookupClass("org.bukkit.entity.Player");
// Check if class exists
boolean exists = ReflectionUtil.isClassAvailable(
"org.bukkit.entity.Phantom"
);
if (exists) {
// Use Phantom features
}
NMS and OBC classes
// Get NMS class (auto-versioned for MC < 1.17)
Class<?> packetClass = ReflectionUtil.getNMSClass("Packet");
// Get NMS class with 1.17+ support
Class<?> entityPlayer = ReflectionUtil.getNMSClass(
"EntityPlayer",
"net.minecraft.server.level.EntityPlayer"
);
// Get OBC (CraftBukkit) class
Class<?> craftPlayer = ReflectionUtil.getOBCClass(
"entity.CraftPlayer"
);
Get all classes from plugin
// Get all classes in plugin
List<Class<?>> allClasses = ReflectionUtil.getClasses(plugin);
// Get classes extending specific class
TreeSet<Class<SimpleCommand>> commands =
ReflectionUtil.getClasses(plugin, SimpleCommand.class);
for (Class<SimpleCommand> cmdClass : commands) {
// Register command
}
Creating instances
Instantiate objects
// Create instance without parameters
MyClass instance = ReflectionUtil.instantiate(MyClass.class);
// Create instance with parameters
MyClass instance = ReflectionUtil.instantiate(
MyClass.class,
"param1",
123,
true
);
// Instantiate from class path
MyClass instance = ReflectionUtil.instantiate(
"org.example.MyClass"
);
// Instantiate NMS class
Object nmsObject = ReflectionUtil.instantiateNMS(
"PacketPlayOutChat",
chatComponent
);
Using constructors
// Get constructor
Constructor<MyClass> constructor =
ReflectionUtil.getConstructor(
MyClass.class,
String.class,
int.class
);
// Create instance from constructor
MyClass instance = ReflectionUtil.instantiate(
constructor,
"test",
42
);
Field operations
Get field values
// Get field from instance
String value = ReflectionUtil.getFieldContent(
object,
"fieldName"
);
// Get static field
String value = ReflectionUtil.getStaticFieldContent(
MyClass.class,
"STATIC_FIELD"
);
// Get declared field
Field field = ReflectionUtil.getDeclaredField(
MyClass.class,
"privateField"
);
Object value = ReflectionUtil.getFieldContent(field, object);
Set field values
// Set instance field
ReflectionUtil.setDeclaredField(
object,
"fieldName",
"newValue"
);
// Set static field
ReflectionUtil.setStaticField(
MyClass.class,
"STATIC_FIELD",
"newValue"
);
Get all fields
// Get all fields including from superclasses
Field[] fields = ReflectionUtil.getAllFields(MyClass.class);
for (Field field : fields) {
Common.log("Field: " + field.getName());
}
Method operations
Get methods
// Get method
Method method = ReflectionUtil.getMethod(
MyClass.class,
"methodName",
String.class,
int.class
);
// Get method without parameters
Method method = ReflectionUtil.getMethod(
MyClass.class,
"simpleMethod"
);
// Get declared method (including private)
Method method = ReflectionUtil.getDeclaredMethod(
MyClass.class,
"privateMethod",
String.class
);
Invoke methods
// Invoke instance method
String result = ReflectionUtil.invoke(
"methodName",
object,
"param1",
123
);
// Invoke static method
String result = ReflectionUtil.invokeStatic(
MyClass.class,
"staticMethod",
"param"
);
// Invoke with method object
Object result = ReflectionUtil.invoke(
method,
object,
"param1",
"param2"
);
Enum handling
Lookup enums
// Lookup enum safely
Material mat = ReflectionUtil.lookupEnum(
Material.class,
"DIAMOND_SWORD"
);
// Lookup with fallback names (for version compatibility)
EntityType type = ReflectionUtil.lookupLegacyEnum(
EntityType.class,
"ZOMBIFIED_PIGLIN",
"PIG_ZOMBIE"
);
// Lookup enum silently (returns null if not found)
Material mat = ReflectionUtil.lookupEnumSilent(
Material.class,
"SOME_ITEM"
);
if (mat != null) {
// Material exists
}
Enum utilities
// Get enum name (works for enum and Keyed interfaces)
String name = ReflectionUtil.getEnumName(Material.DIAMOND);
// Result: "DIAMOND"
// Get all enum values
Material[] values = ReflectionUtil.getEnumValues(Material.class);
Debugging
// Get caller method info (useful for debugging)
String callers = ReflectionUtil.getCallerMethods(0, 3);
// Result: "MyClass#25-myMethod().OtherClass#42-otherMethod()"
Examples
Dynamic command registration
public class CommandLoader {
public void loadCommands() {
// Find all command classes
TreeSet<Class<SimpleCommand>> commands =
ReflectionUtil.getClasses(
SimplePlugin.getInstance(),
SimpleCommand.class
);
for (Class<SimpleCommand> cmdClass : commands) {
try {
// Create instance
SimpleCommand command =
ReflectionUtil.instantiate(cmdClass);
// Register
SimpleCommandGroup.registerCommand(command);
Common.log("Registered command: " +
cmdClass.getSimpleName());
} catch (Exception ex) {
Common.error(ex,
"Failed to load command: " +
cmdClass.getName()
);
}
}
}
}
Version-compatible code
public class VersionHandler {
private static Method getHandleMethod;
private static Method sendPacketMethod;
static {
try {
// Get methods for packet sending
Class<?> craftPlayer = ReflectionUtil.getOBCClass(
"entity.CraftPlayer"
);
getHandleMethod = ReflectionUtil.getMethod(
craftPlayer,
"getHandle"
);
Class<?> entityPlayer = ReflectionUtil.getNMSClass(
"EntityPlayer",
"net.minecraft.server.level.EntityPlayer"
);
Class<?> playerConnection = ReflectionUtil.getNMSClass(
"PlayerConnection",
"net.minecraft.server.network.PlayerConnection"
);
// Find sendPacket method
for (Method method : playerConnection.getMethods()) {
if (method.getName().equals("sendPacket")) {
sendPacketMethod = method;
break;
}
}
} catch (Exception ex) {
Common.error(ex, "Failed to initialize version handler");
}
}
public static void sendPacket(Player player, Object packet) {
try {
Object handle = ReflectionUtil.invoke(
getHandleMethod,
player
);
Object connection = ReflectionUtil.getFieldContent(
handle,
MinecraftVersion.atLeast(V.v1_17) ?
"b" : "playerConnection"
);
ReflectionUtil.invoke(
sendPacketMethod,
connection,
packet
);
} catch (Exception ex) {
Common.error(ex, "Failed to send packet");
}
}
}
Configuration with enum fallback
public class ConfigManager {
public Material getMaterial(String path) {
String value = config.getString(path);
try {
return ReflectionUtil.lookupEnum(
Material.class,
value
);
} catch (MissingEnumException ex) {
Common.warning(
"Invalid material in config: " + value +
" at " + path
);
return Material.STONE; // Default
}
}
public EntityType getEntityType(String path, EntityType def) {
String value = config.getString(path);
// Use silent lookup (returns null if not found)
EntityType type = ReflectionUtil.lookupEnumSilent(
EntityType.class,
value
);
return type != null ? type : def;
}
}
Dynamic plugin feature loading
public abstract class Feature {
public abstract void enable();
public abstract void disable();
}
public class FeatureManager {
private final List<Feature> features = new ArrayList<>();
public void loadFeatures() {
// Find all feature classes
TreeSet<Class<Feature>> featureClasses =
ReflectionUtil.getClasses(
SimplePlugin.getInstance(),
Feature.class
);
for (Class<Feature> featureClass : featureClasses) {
// Skip abstract classes
if (Modifier.isAbstract(featureClass.getModifiers())) {
continue;
}
try {
// Create and enable feature
Feature feature = ReflectionUtil.instantiate(
featureClass
);
feature.enable();
features.add(feature);
Common.log("Loaded feature: " +
featureClass.getSimpleName());
} catch (Exception ex) {
Common.error(ex,
"Failed to load feature: " +
featureClass.getName()
);
}
}
}
public void disableFeatures() {
for (Feature feature : features) {
try {
feature.disable();
} catch (Exception ex) {
Common.error(ex, "Error disabling feature");
}
}
features.clear();
}
}
Reflection operations can throw
ReflectionException if classes, methods, or fields are not found.Use
ReflectionUtil.lookupEnumSilent() when loading enums from config files to gracefully handle version differences.