How do you describe a method's signature with MethodType, and how does it relate to building and adapting MethodHandles?
answer
- MethodType = return type + params, no name, immutable + interned
- methodType(rtype, ptypes...) ; return type first
- findVirtual: receiver added as leading param on the handle
- findConstructor: type returns void, handle returns the object
- asType reshapes a handle via allowed conversions (basis of invoke)
basics
~20 sMethodType is an immutable object describing a signature: the return type plus the parameter types, with no name. You pass it to find* methods to say which overload you want, and you can derive new MethodTypes (change a parameter, drop one) to adapt a handle's shape.
solid answer
~50 sA MethodType captures a signature as a return type plus an ordered list of parameter types - never a name - and is interned/immutable, so equal signatures are the same instance. You build one with MethodType.methodType(returnType, paramTypes...) (or genericMethodType, fromMethodDescriptorString). When you call findVirtual/findStatic/findConstructor you pass the MethodType to disambiguate the exact member; note that for findVirtual the receiver is NOT in the MethodType you pass (it is added as a leading parameter on the resulting handle), whereas for findConstructor the type's return must be void but the handle returns the constructed type. MethodType also has transformer methods - changeReturnType, changeParameterType, insertParameterTypes, dropParameterTypes, appendParameterTypes - that produce new MethodTypes. These pair with handle adapters and especially asType: handle.asType(newType) returns a handle adapted to a different MethodType using the allowed conversions, which is exactly how invoke reconciles call-site types and how you pre-shape a handle for invokeExact.
go deeper
Can say MethodType is a signature (return + params) you pass to find* methods.
Builds MethodTypes correctly, knows the return-type-first ordering, and that findVirtual adds the receiver as a leading parameter.
Uses transformer methods and asType to adapt handle shapes, understands the constructor-returns-void rule, and links asType to invoke's conversion behaviour.
Designs adapter pipelines (insert/drop/filter/fold) reasoning purely in MethodType transformations and uses interning for call-site caching.
### What a MethodType is `MethodType` is an immutable, value-like object that represents a **method signature**: one **return type** followed by zero or more **parameter types**, in order. Crucially it carries **no method name and no owning class** - just the shape. Two MethodTypes are equal iff their return type and parameter types match, and the JVM **interns** them, so equal signatures share one instance (cheap `==` comparison, good for cache keys). Examples (return type comes first): - `MethodType.methodType(int.class, int.class, int.class)` -> `(int,int)int` - `MethodType.methodType(void.class)` -> `()void` - `MethodType.methodType(String.class)` -> `()String` ### How you construct one - `MethodType.methodType(rtype, ptype...)` - the workhorse. - `MethodType.methodType(rtype)` - no parameters. - `MethodType.methodType(rtype, MethodType other)` - reuse another type's params. - `MethodType.genericMethodType(n)` - `n` `Object` params returning `Object` (handy for generic adapters). - `MethodType.fromMethodDescriptorString("(II)I", loader)` - parse a JVM descriptor. ### Its role when finding a handle The `find*` methods take the **owning class**, the **member name**, and a **MethodType** to pin down the exact overload: ```java lookup.findStatic(Math.class, "max", MethodType.methodType(int.class, int.class, int.class)); // picks max(int,int) ``` Two subtleties: - **findVirtual / findSpecial:** the MethodType you pass describes the method **as declared** (without the receiver). The receiver is **prepended** as a leading parameter on the *resulting handle's* type. So the handle's `type()` has one more parameter than the MethodType you supplied. - **findConstructor:** the MethodType's **return type must be `void`** (it describes the constructor's parameters), but the resulting handle **returns the newly constructed object**. - **findGetter/findSetter:** you pass the **field type** (not a MethodType) plus owning class and name. ### Transforming MethodTypes Because it is immutable, every "change" returns a **new** MethodType: - `changeReturnType(t)` - `changeParameterType(i, t)` - `insertParameterTypes(i, t...)` - `dropParameterTypes(start, end)` - `appendParameterTypes(t...)` - `erase()` / `wrap()` / `unwrap()` - reference-erase or box/unbox the shape. ### The bridge to handles: asType MethodType is what handle adapters speak in. The central one is **`MethodHandle.asType(MethodType newType)`**: it returns a *new handle* that accepts/returns `newType`, inserting the **allowed conversions** (widening, boxing/unboxing, reference casts, void adaptation) between the new shape and the original. This is exactly the mechanism `invoke` uses under the hood to reconcile a call site with the handle, and it lets you **pre-adapt** a handle so you can then use the faster `invokeExact`: ```java MethodHandle generic = mh.asType(MethodType.genericMethodType(2)); // (Object,Object)Object Object r = generic.invokeExact((Object) a, (Object) b); ``` Other structural adapters change the *number* of parameters and therefore the MethodType: `MethodHandles.insertArguments` (bind values), `dropArguments` (ignore extra args), `filterArguments`/`foldArguments` (pre-process args). Each yields a handle with a correspondingly transformed `type()`. ### Why this matters Getting a handle to work hinges on matching MethodTypes. The most common confusions - "why does my virtual handle have an extra parameter?", "why does my constructor type return void?", "why does invokeExact throw?" - all dissolve once you think in MethodTypes and remember the receiver-as-leading-parameter and constructor-return rules. ### Mental model A MethodType is a **plug shape diagram** with no label. `find*` uses the diagram to pick the right socket; `asType` and the argument adapters are machines that reshape one diagram into another using only legal bends (widen/box/cast), producing a new handle that fits a new socket.
- Why does a handle from findVirtual have one more parameter than the MethodType you passed?Because the receiver (the instance the method is called on) is prepended as the leading parameter of the resulting handle's type; the MethodType you pass describes only the declared parameters.
- How does asType relate to invoke?invoke effectively performs handle.asType(callSiteType) before dispatching, applying the allowed conversions. You can call asType yourself once to bake the conversions into a reusable handle and then use the faster invokeExact.
saying these in an interview costs you the question
- Putting the method name into MethodType
- Including the receiver in the MethodType passed to findVirtual
- Expecting a constructor MethodType to have the class as its return type
- Thinking transformer methods mutate the MethodType in place (they return new ones)