java getDeclaredMethods: How to Use Reflection
java getdeclaredmethods: Learn how to use getDeclaredMethods() in Java reflection to inspect class methods, access private methods, and avoid common pitfalls.
java getdeclaredmethods requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
When you need to inspect the methods of a Java class at runtime, getDeclaredMethods() is the reflection API you'll likely use. It returns a Method[] containing all methods declared directly in the class—regardless of access modifier—but excludes inherited methods. This makes it the primary tool for discovering private, protected, package-private, and public methods that are defined in the class itself, rather than those inherited from a superclass or interface.
What getDeclaredMethods() Returns
The Class.getDeclaredMethods() method returns an array of Method objects representing all methods declared by the class or interface. This includes static, instance, abstract, final, and native methods. It does not include methods inherited from a superclass, nor default methods from interfaces unless the class itself overrides or re-declares them.
For example, given the following class:
public class UserService { public void createUser() { } private void validateEmail() { } protected void logAccess() { } void sendNotification() { } }
Calling UserService.class.getDeclaredMethods() returns an array containing all four methods, even though validateEmail is private and sendNotification is package-private. The order of the array is not specified by the JLS; it depends on the JVM implementation and can vary between runs.
Accessing Private and Non-Public Methods
Because getDeclaredMethods() includes non-public methods, you can invoke them using reflection, but you must first call setAccessible(true) on the Method object. This suppresses Java language access checks for that specific method. Without it, invoking a private method throws an IllegalAccessException.
Method validateEmail = UserService.class.getDeclaredMethod("validateEmail"); validateEmail.setAccessible(true); validateEmail.invoke(new UserService());
This works in a plain Java application, but the module system introduced in Java 9 adds restrictions. If the class is in a named module, you must open the package to the caller's module using --add-opens or an opens directive in the module descriptor. Otherwise, setAccessible(true) may throw an InaccessibleObjectException.
Method Order and Determinism
The JVM does not guarantee any specific order for the array returned by getDeclaredMethods(). The order can differ across JVM versions, or even between runs of the same program. Therefore, you should never rely on array indices to identify a specific method. Instead, filter by method name, parameter types, or annotations.
If you need a stable ordering, sort the array manually. For example, you can sort by method name and parameter count:
Method[] methods = clazz.getDeclaredMethods(); Arrays.sort(methods, Comparator.comparing(Method::getName));
Sorting is particularly important when you use reflection to generate documentation, build serialization logic, or implement dependency injection that must behave consistently across environments.
Handling Synthetic and Bridge Methods
getDeclaredMethods() includes synthetic methods generated by the compiler for purposes like nested class access or enum switch statements. It also includes bridge methods, which are synthetic methods that preserve polymorphism when generic types are erased. These methods are marked with the synthetic or bridge modifiers, which you can check using Method.isSynthetic() or Method.isBridge().
For most reflection-based tools, you'll want to filter these out to avoid exposing implementation details. Here's a common filtering pattern:
List<Method> userMethods = Arrays.stream(clazz.getDeclaredMethods()) .filter(m -> !m.isSynthetic() && !m.isBridge()) .collect(Collectors.toList());
This keeps only the methods that the developer actually wrote. Failing to filter can lead to confusing results, such as seeing extra access$000 methods or generic bridge methods that duplicate your source-level methods.
Performance and Caching Considerations
Reflection is significantly slower than direct method calls because the JVM must perform dynamic lookup and access checks. Calling getDeclaredMethods() itself is relatively cheap, but invoking the returned Method objects repeatedly can become a bottleneck. If your code inspects the same class multiple times, cache the Method[] array or the individual Method objects rather than re-querying the class each time.
private static final Method[] USER_METHODS = UserService.class.getDeclaredMethods();
Be aware that caching Method objects does not make invocation faster; it only avoids the repeated class metadata lookup. For performance-critical paths, consider using java.lang.invoke.MethodHandle or a code-generation library like Byte Buddy, which can offer better runtime performance than classic reflection.
Common Pitfalls with getDeclaredMethods()
One frequent mistake is assuming that getDeclaredMethods() returns methods from superclasses. It does not. If you need the full set of accessible methods, including inherited public methods, use getMethods() instead. However, getMethods() only returns public methods, so you lose access to non-public inherited methods.
Another pitfall is ignoring exceptions. getDeclaredMethods() throws SecurityException if a security manager is present and denies access. In modern Java, security managers are deprecated, but they can still appear in legacy environments. Also, when dealing with array classes or primitive types, getDeclaredMethods() returns an empty array because those types do not declare methods.
Finally, be careful when calling setAccessible(true) on methods from classes in unnamed modules. While it works for most application classes, it fails for classes in the Java platform itself unless the module is explicitly opened. This is why you cannot easily reflectively invoke private methods in java.lang classes without JVM flags.
Practical Example: Inspecting a Class's Methods
Here's a complete example that lists all non-synthetic, non-bridge methods declared by a class, showing their modifiers and parameter types:
import java.lang.reflect.*; import java.util.*; import java.util.stream.*; public class MethodInspector { public static void main(String[] args) { Class<?> clazz = UserService.class; List<Method> methods = Arrays.stream(clazz.getDeclaredMethods()) .filter(m -> !m.isSynthetic() && !m.isBridge()) .collect(Collectors.toList()); for (Method method : methods) { System.out.println(Modifier.toString(method.getModifiers()) + " " + method.getReturnType().getSimpleName() + " " + method.getName() + "(" + Arrays.stream(method.getParameterTypes()) .map(Class::getSimpleName) .collect(Collectors.joining(", ")) + ")"); } } }
This code filters out compiler-generated methods and prints a readable signature for each declared method. It works for any class you pass to it, making it a useful utility for debugging or building framework-like features.
When to Use getDeclaredMethods() Instead of getMethods()
The choice between getDeclaredMethods() and getMethods() depends on what you need. Use getDeclaredMethods() when you must access non-public methods, or when you need to distinguish between methods declared in the class and those inherited. This is common in frameworks that scan for annotations on private methods, such as JUnit's @BeforeEach or Spring's @Autowired on setter methods.
Use getMethods() when you only need public methods, including inherited ones, and you don't care about the declaring class. This is simpler and avoids the need to filter synthetic methods, since getMethods() also excludes non-public methods.
The table below summarizes the key differences:
| Criterion | getDeclaredMethods() | getMethods() |
|---|---|---|
| Includes private methods | Yes | No |
| Includes inherited methods | No | Yes (public only) |
| Includes synthetic methods | Yes, unless filtered | Yes, unless filtered |
| Typical use case | Full class introspection | Public API discovery |
In practice, most reflection-heavy libraries use getDeclaredMethods() because they need access to non-public methods and can filter inherited methods manually when required. Understanding the exact behavior of this method helps you avoid subtle bugs when building serializers, ORMs, or test runners that rely on runtime method discovery.