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,32 @@
|
||||
$ java -cp target/classes com.ankurm.reflection.ReflectionBasicsDemo
|
||||
|
||||
== 1. Inspect the class ==
|
||||
class: com.ankurm.reflection.Account
|
||||
public long balance[]
|
||||
public void deposit[long]
|
||||
public String owner[]
|
||||
private String secret[]
|
||||
public String[] stack[]
|
||||
public String toString[]
|
||||
public static int twice[int]
|
||||
|
||||
== 2. Create and call ==
|
||||
created: Account[alice, 50]
|
||||
after deposit(25): Account[alice, 75]
|
||||
|
||||
== 3. The exceptions you will meet ==
|
||||
wrong name -> NoSuchMethodException: com.ankurm.reflection.Account.nope()
|
||||
wrong arg type -> IllegalArgumentException: argument type mismatch
|
||||
method threw -> InvocationTargetException wrapping java.lang.IllegalArgumentException: over limit: 5000
|
||||
private, no opt-in -> IllegalAccessException: class com.ankurm.reflection.ReflectionBasicsDemo cannot access a member of class com.ankurm.reflection.Account with modifiers "private"
|
||||
after setAccessible(true): secret-of-alice
|
||||
|
||||
== 4. What reflection adds to the stack ==
|
||||
direct call:
|
||||
com.ankurm.reflection.Account.stack
|
||||
com.ankurm.reflection.ReflectionBasicsDemo.main
|
||||
through Method.invoke:
|
||||
com.ankurm.reflection.Account.stack
|
||||
jdk.internal.reflect.DirectMethodHandleAccessor.invoke
|
||||
java.lang.reflect.Method.invoke
|
||||
com.ankurm.reflection.ReflectionBasicsDemo.main
|
||||
@@ -0,0 +1,24 @@
|
||||
$ java -cp target/classes com.ankurm.reflection.ModuleRulesDemo
|
||||
|
||||
== setAccessible on your own class ==
|
||||
Account.owner = carol
|
||||
|
||||
== setAccessible on a JDK private field ==
|
||||
String lives in module : java.base
|
||||
we live in module : unnamed module @6b95977 (unnamed=true)
|
||||
java.lang open to us : false
|
||||
trySetAccessible : false
|
||||
setAccessible threw : InaccessibleObjectException
|
||||
Unable to make field private final byte[] java.lang.String.value accessible: module java.base does not "opens java.lang" to unnamed module @6b95977
|
||||
|
||||
$ java --add-opens java.base/java.lang=ALL-UNNAMED -cp target/classes com.ankurm.reflection.ModuleRulesDemo
|
||||
|
||||
== setAccessible on your own class ==
|
||||
Account.owner = carol
|
||||
|
||||
== setAccessible on a JDK private field ==
|
||||
String lives in module : java.base
|
||||
we live in module : unnamed module @3764951d (unnamed=true)
|
||||
java.lang open to us : true
|
||||
trySetAccessible : true
|
||||
setAccessible worked, String.value is a byte[]
|
||||
@@ -0,0 +1,47 @@
|
||||
$ java -cp target/classes com.ankurm.reflection.FinalFieldDemo # JDK 25
|
||||
|
||||
java.version = 25.0.5
|
||||
|
||||
== 1. final instance field ==
|
||||
owner() now: mallory
|
||||
|
||||
== 2. static final field ==
|
||||
IllegalAccessException: Can not set static final int field com.ankurm.reflection.Account.MAX_DEPOSIT to java.lang.Integer
|
||||
|
||||
== 3. record component ==
|
||||
IllegalAccessException: Can not set final int field com.ankurm.reflection.FinalFieldDemo$Point.x to java.lang.Integer
|
||||
|
||||
== 4. constant inlining: the write 'works' but the getter never notices ==
|
||||
Field.get -> 99
|
||||
max() -> 5
|
||||
|
||||
$ java -cp target/classes com.ankurm.reflection.FinalFieldDemo # JDK 26
|
||||
|
||||
java.version = 26.0.2.1
|
||||
|
||||
== 1. final instance field ==
|
||||
WARNING: Final field owner in class com.ankurm.reflection.Account has been mutated reflectively by class com.ankurm.reflection.FinalFieldDemo in unnamed module @18b4aac2 (file:/tmp/work/java-core-examples/reflection/target/classes/)
|
||||
WARNING: Use --enable-final-field-mutation=ALL-UNNAMED to avoid a warning
|
||||
WARNING: Mutating final fields will be blocked in a future release unless final field mutation is enabled
|
||||
owner() now: mallory
|
||||
|
||||
== 2. static final field ==
|
||||
IllegalAccessException: Can not set static final int field com.ankurm.reflection.Account.MAX_DEPOSIT to java.lang.Integer
|
||||
|
||||
== 3. record component ==
|
||||
IllegalAccessException: Can not set final int field com.ankurm.reflection.FinalFieldDemo$Point.x to java.lang.Integer
|
||||
|
||||
== 4. constant inlining: the write 'works' but the getter never notices ==
|
||||
Field.get -> 99
|
||||
max() -> 5
|
||||
|
||||
$ java --illegal-final-field-mutation=deny -cp target/classes com.ankurm.reflection.FinalFieldDemo # JDK 26
|
||||
|
||||
java.version = 26.0.2.1
|
||||
|
||||
== 1. final instance field ==
|
||||
Exception in thread "main" java.lang.IllegalAccessException: class com.ankurm.reflection.FinalFieldDemo (in unnamed module @18b4aac2) cannot set final field com.ankurm.reflection.Account.owner (in unnamed module @18b4aac2), unnamed module @18b4aac2 is not allowed to mutate final fields
|
||||
at java.base/java.lang.reflect.Field.preSetFinal(Field.java:1504)
|
||||
at java.base/java.lang.reflect.Field.setFinal(Field.java:1447)
|
||||
at java.base/java.lang.reflect.Field.set(Field.java:909)
|
||||
at com.ankurm.reflection.FinalFieldDemo.main(FinalFieldDemo.java:30)
|
||||
@@ -0,0 +1,31 @@
|
||||
$ java -cp target/classes com.ankurm.reflection.MethodHandlesDemo
|
||||
|
||||
== 1. Look up once, call many times ==
|
||||
twice(21) = 42
|
||||
balance() = 50
|
||||
handle type = (Account)long
|
||||
|
||||
== 2. invokeExact is exact; invoke adapts ==
|
||||
invokeExact, wrong return type -> WrongMethodTypeException
|
||||
handle's method type (Account)long but found (Account)Object
|
||||
invoke (boxing) = 50 (Long)
|
||||
|
||||
== 3. Handles compose ==
|
||||
bindTo(acct) = 50
|
||||
after deposit10 = Account[erin, 60]
|
||||
balance as text = "60"
|
||||
|
||||
== 4. Access is checked when you look up, not when you call ==
|
||||
findVirtual(private) -> IllegalAccessException
|
||||
no such method: com.ankurm.reflection.Account.secret()String/invokeVirtual
|
||||
privateLookupIn -> secret-of-erin
|
||||
privateLookupIn(String) -> IllegalAccessException
|
||||
module java.base does not open java.lang to unnamed module @135fbaa4
|
||||
|
||||
== 5. VarHandle: typed, atomic field access ==
|
||||
get = 60
|
||||
CAS(60 -> 100) = true, balance now 100
|
||||
CAS(60 -> 200) = false, balance now 100
|
||||
getAndAdd(5) = was 100, now 105
|
||||
set on final field -> UnsupportedOperationException
|
||||
array element CAS-able: counts[1] = 7
|
||||
@@ -0,0 +1,15 @@
|
||||
$ java -jar target/benchmarks.jar CallBenchmark
|
||||
|
||||
openjdk version "25.0.5" 2026-10-20 LTS
|
||||
|
||||
Benchmark Mode Cnt Score Error Units
|
||||
CallBenchmark.direct avgt 10 0.516 ± 0.069 ns/op
|
||||
CallBenchmark.field_get avgt 10 0.899 ± 0.051 ns/op
|
||||
CallBenchmark.methodHandle_instanceField_invokeExact avgt 10 2.600 ± 0.145 ns/op
|
||||
CallBenchmark.methodHandle_staticFinal_invokeBoxing avgt 10 2.690 ± 0.262 ns/op
|
||||
CallBenchmark.methodHandle_staticFinal_invokeExact avgt 10 0.476 ± 0.038 ns/op
|
||||
CallBenchmark.reflection_instanceFieldMethod avgt 10 6.476 ± 1.952 ns/op
|
||||
CallBenchmark.reflection_lookupEveryCall avgt 10 21.525 ± 1.756 ns/op
|
||||
CallBenchmark.reflection_staticFinalMethod avgt 10 1.208 ± 0.152 ns/op
|
||||
CallBenchmark.varHandle_staticFinal_get avgt 10 0.519 ± 0.076 ns/op
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
$ java src/main/java/com/ankurm/reflection/QuickStart.java
|
||||
|
||||
reflection : Hello, Ankur
|
||||
MethodHandle : Hello, Ankur
|
||||
Reference in New Issue
Block a user