reflection: Method/Field/MethodHandle/VarHandle demos, module and final-field rules (JEP 500 on JDK 26), JMH call-cost numbers, contract tests
Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_017ic4b6NtPJHrkan4BXhCAM
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
# reflection
|
||||
|
||||
Companion code for the ankurm.com post *"Java Reflection and MethodHandles in 2026: How They Work
|
||||
and What They Cost."* One module in `java-core-examples`. Everything the post explains lives in
|
||||
the post itself (accordions per section); this module is the runnable evidence.
|
||||
|
||||
## Versions this was built and tested against
|
||||
|
||||
| Component | Version | Notes |
|
||||
|---|---|---|
|
||||
| JDK | 25.0.5+8 (Temurin, LTS) | All demos and the JMH run. |
|
||||
| JDK (comparison only) | 26.0.2.1+1 (Temurin) | Used for the JEP 500 final-field warning in `output/03`. |
|
||||
| JMH | 1.37 | 3 warmup + 5 measurement iterations, 2 forks. |
|
||||
| JUnit Jupiter | 5.11.0 | Contract tests - they fail if a documented behaviour changes. |
|
||||
| Maven | 3.9.11 | |
|
||||
| Hardware | 2 vCPU Intel Xeon @ 2.30 GHz VM | Shared sandbox: treat ns figures as relative, not absolute. |
|
||||
|
||||
## Quickstart
|
||||
|
||||
```bash
|
||||
export JAVA_HOME=/path/to/jdk-25
|
||||
mvn package # compiles, runs the contract tests, builds the JMH jar
|
||||
java src/main/java/com/ankurm/reflection/QuickStart.java
|
||||
java -cp target/classes com.ankurm.reflection.ReflectionBasicsDemo
|
||||
java -cp target/classes com.ankurm.reflection.ModuleRulesDemo
|
||||
java --add-opens java.base/java.lang=ALL-UNNAMED -cp target/classes com.ankurm.reflection.ModuleRulesDemo
|
||||
java -cp target/classes com.ankurm.reflection.FinalFieldDemo
|
||||
java -cp target/classes com.ankurm.reflection.MethodHandlesDemo
|
||||
java -jar target/benchmarks.jar CallBenchmark
|
||||
```
|
||||
|
||||
`scripts/run-all.sh` regenerates every file in `output/` (set `JDK26_HOME` to include the JDK 26 runs).
|
||||
|
||||
## What's in here
|
||||
|
||||
| File | What it shows |
|
||||
|---|---|
|
||||
| `Account.java` | The sample class: public API, private method, private/final fields, a throwing method. |
|
||||
| `QuickStart.java` | Self-contained: one call by `Method`, one by `MethodHandle`. Run with `java QuickStart.java`. |
|
||||
| `ReflectionBasicsDemo.java` | Inspect, construct, invoke; the five exceptions you will meet; extra stack frames. |
|
||||
| `ModuleRulesDemo.java` | `setAccessible` on your class vs. a JDK internal; `--add-opens`. |
|
||||
| `FinalFieldDemo.java` | Final instance / static / record fields; constant-inlining trap; JEP 500 on JDK 26. |
|
||||
| `MethodHandlesDemo.java` | `invokeExact` vs `invoke`, composition, lookup-time access checks, `VarHandle`. |
|
||||
| `CallBenchmark.java` | JMH: direct vs `Method` vs `MethodHandle` vs `Field` vs `VarHandle`, static-final vs instance field. |
|
||||
| `ReflectionContractTest.java` | Eight tests pinning the contracts the post states. |
|
||||
|
||||
## Captured output
|
||||
|
||||
| File | From |
|
||||
|---|---|
|
||||
| `output/01-basics.txt` | `ReflectionBasicsDemo` |
|
||||
| `output/02-module-rules.txt` | `ModuleRulesDemo`, without and with `--add-opens` |
|
||||
| `output/03-final-fields.txt` | `FinalFieldDemo` on JDK 25, JDK 26, and JDK 26 with `--illegal-final-field-mutation=deny` |
|
||||
| `output/04-method-handles.txt` | `MethodHandlesDemo` |
|
||||
| `output/05-call-benchmark.txt` | `CallBenchmark` (non-deterministic; relative ordering is the result) |
|
||||
| `output/06-quickstart.txt` | `QuickStart` |
|
||||
|
||||
Identity-hash suffixes like `unnamed module @6b95977` change on every run.
|
||||
Reference in New Issue
Block a user