How does getMethod / getDeclaredMethod identify which overloaded method to return, and what is returned for inherited or interface default methods?
answer
- Disambiguate overloads by exact param Class types
- Primitives: int.class not Integer.class
- Match is exact type, not assignable
- getMethod = public + inherited + interfaces
- Watch isBridge()/isSynthetic() from erasure
basics
~20 sYou pass the method name plus the parameter Class types, so Java picks the exact matching overload. getMethod finds public methods including inherited and interface ones; getDeclaredMethod finds any-access methods declared in that one class only.
solid answer
~40 sOverloaded methods share a name but differ by parameter list, so getMethod(name, Class...)/getDeclaredMethod(name, Class...) take the declared parameter types to disambiguate; you must pass the exact erased parameter types (e.g. List.class, not ArrayList.class, if that's how it's declared), and primitives as int.class etc. getMethod returns public methods and searches superclasses and implemented interfaces, so it can return an inherited method or an interface's public/default method. getDeclaredMethod returns methods of any access but only those declared in that exact class — it won't return an inherited or interface method, and it sees private/synthetic/bridge methods. Because generics are erased, you may encounter compiler-generated bridge methods; check isBridge()/isSynthetic() if you need the real one. Argument matching is by exact type, not by assignability, so passing a subtype of the declared parameter type fails to match.
code
java · 16 linesclass Box implements Comparable<Box> {
void put(int x) {}
void put(Integer x) {} // overload differs by param type
public int compareTo(Box o) { return 0; }
}
// Disambiguate overloads by exact parameter Class:
Method mInt = Box.class.getMethod("put", int.class); // void put(int)
Method mBox = Box.class.getMethod("put", Integer.class); // void put(Integer)
// Generics erasure adds a bridge compareTo(Object):
for (Method m : Box.class.getDeclaredMethods()) {
if (m.getName().equals("compareTo") && !m.isBridge()) {
// this is the real user-declared compareTo(Box)
}
}go deeper
Knows you pass the method name plus parameter types to pick an overload, and that getMethod is public-only.
Knows primitives use int.class, that getMethod includes inherited/interface methods while getDeclaredMethod is this-class-only, and that matching is by exact type.
Explains erasure's effect (raw types, bridge/synthetic methods) and filters them; knows default methods surface via getMethod and matching is non-assignable exact type.
Reasons about resolving the most-specific method across multiple supertypes, invoking specific interface defaults via MethodHandles, and the performance/caching strategy for hot reflective dispatch.
## Overloading recap **Overloading** means several methods in a class share the same **name** but differ in their **parameter list** (number/types of parameters). At compile time, Java's source compiler picks the right one. Reflection has no source context, so *you* must specify which overload you want. ## How you disambiguate Both lookups take a name **and** the parameter types: ``` getMethod(String name, Class<?>... parameterTypes) getDeclaredMethod(String name, Class<?>... parameterTypes) ``` The `parameterTypes` must be the **exact declared types** of the formal parameters: - Primitives use their `Class` literal: `int.class`, `boolean.class`, `long.class` (NOT `Integer.class`). - The match is by **exact type identity, not assignability**: if the parameter is declared `List`, you must pass `List.class`; passing `ArrayList.class` will throw `NoSuchMethodException` even though an `ArrayList` *is* a `List`. - Because generics are **erased** at runtime (`List<String>` becomes plain `List`), you pass the raw erased type — there is no `List<String>.class`. ## What each lookup searches - **`getMethod`**: returns **public** methods and searches the whole type graph — this class, its superclasses, and all **implemented interfaces**. So it can return an **inherited** method or an **interface default/abstract public** method. If several supertypes provide a matching signature, the JLS/JVM has rules to pick the most specific. - **`getDeclaredMethod`**: returns methods of **any access level** but **only those physically declared in this exact class**. It will **not** return an inherited or interface method, and it *will* see `private`, package-private, `synthetic`, and `bridge` methods declared here. ## Bridge and synthetic methods Due to generics erasure and covariant return types, the compiler sometimes inserts extra methods: - A **bridge method** preserves polymorphism after erasure (e.g. a generic `compareTo(T)` gets a `compareTo(Object)` bridge). - **Synthetic** members are any compiler-generated members. `getDeclaredMethods()` includes these; when you reflect over methods you often filter with `method.isBridge()` and `method.isSynthetic()` to get the "real" one. A naive `getDeclaredMethod` by erased signature can return the bridge rather than the user-written method. ## Default methods An **interface default method** is a public method with a body declared in an interface. Because it is public and inherited into implementers, `getMethod` on an implementing class can return it. Invoking a *specific* interface default (e.g. to call the super-interface version) requires `MethodHandles` / `invokespecial` tricks, but plain listing/finding sees it as a public method. ## Practical pitfalls - Forgetting that primitives need `int.class` not `Integer.class`. - Passing a subtype and expecting a match (it won't — exact types only). - Getting a bridge method back and invoking it (usually works but loses type info; filter it out if you need the declared one). - Order of `getDeclaredMethods()` / `getMethods()` arrays is unspecified.
- Why must you pass int.class rather than Integer.class for a primitive parameter?The parameter type match is by exact runtime Class identity; a method declared with int has parameter type int.class, and Integer.class is a different, boxed type that won't match.
- Where do bridge methods come from and how do you skip them?The compiler generates them to preserve polymorphism after generics erasure or for covariant returns; filter them with method.isBridge() (and isSynthetic()) to keep the user-declared method.
saying these in an interview costs you the question
- Passing Integer.class for an int parameter
- Passing a subtype expecting it to match the declared supertype param
- Thinking getDeclaredMethod returns inherited or interface methods
- Ignoring bridge methods and invoking the wrong one