Skip to content

Commit 582a1da

Browse files
committed
JDK 24: added example of Class File API transformation
1 parent 5234b21 commit 582a1da

6 files changed

+99
-2
lines changed

java-21/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -340,4 +340,5 @@ Deprecations:
340340
* [Java 21 Brings Full Pattern Matching](https://www.youtube.com/watch?v=QrwFrm1R8OY)
341341
* [Java's Virtual Threads - Next Steps](https://www.youtube.com/watch?v=KBW4LbCoo6c)
342342
* [Continuations: The magic behind virtual threads in Java by Balkrishna Rawool @ Spring I/O 2024](https://www.youtube.com/watch?v=pwLtYvRK334)
343+
* [Sequenced Collections - Deep Dive with the Expert](https://www.youtube.com/watch?v=6yuDqkkYTGU)
343344

java-24/BasicClass.java

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import java.io.Serializable;
22

3+
/**
4+
* Used in Class File API examples.
5+
*/
36
public class BasicClass implements Serializable {
47
private String attribute;
58
private int timestamp;

java-24/ClassFileApiReadingExample.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import java.util.stream.Collectors;
77

88
/**
9-
* Run: `javac BasicClass.java && java --source 24 --enable-preview ClassFileApiReadingExample.java`
9+
* Run: `javac BasicClass.java && java ClassFileApiReadingExample.java`
1010
*/
1111
public class ClassFileApiReadingExample {
1212
public static void main(String[] args) throws Exception {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* Used in Class File API transforming example.
3+
*/
4+
public class ClassFileApiTestTransformedBasicClass {
5+
public static void main(String[] args) {
6+
System.out.println("Using the transformed BasicClass");
7+
var obj = new BasicClass("The next arg will not be returned", 10203040, true);
8+
// won't exists after transformation
9+
// obj.setTimestamp(123);
10+
System.out.println("Return of getTimestamp: " + obj.getTimestamp());
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import java.lang.classfile.*;
2+
import java.lang.constant.*;
3+
import java.nio.file.Files;
4+
import java.nio.file.Paths;
5+
6+
/**
7+
* CodeBuilder doc: https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/lang/classfile/CodeBuilder.html
8+
*
9+
* - compile BasicClass and ClassFileApiTestTransformedBasicClass classes
10+
* - run transformation code in the BasicClass bytes
11+
* - run BasicClass testing class
12+
*
13+
* Run: `javac ClassFileApiTestTransformedBasicClass.java && java ClassFileApiTransformingExample.java && java ClassFileApiTestTransformedBasicClass`
14+
*/
15+
public class ClassFileApiTransformingExample {
16+
public static void main(String[] args) throws Exception {
17+
var classBytes = Files.readAllBytes(Paths.get("BasicClass.class"));
18+
19+
var transformedClassBytes = dropMethod(classBytes);
20+
transformedClassBytes = transformMethod(transformedClassBytes);
21+
22+
writeFile(transformedClassBytes);
23+
}
24+
25+
static byte[] dropMethod(byte[] classBytes) {
26+
System.out.println("Dropping setters");
27+
ClassModel originalClassModel = ClassFile.of().parse(classBytes);
28+
byte[] transformedClassBytes = ClassFile.of().build(
29+
originalClassModel.thisClass().asSymbol(),
30+
classBuilder -> {
31+
for (ClassElement ce : originalClassModel) {
32+
// removes every setter
33+
if (!(ce instanceof MethodModel mm && mm.methodName().stringValue().startsWith("set"))) {
34+
classBuilder.with(ce);
35+
}
36+
}
37+
}
38+
);
39+
40+
// there are helpful methods in ClassFile to do the transformations
41+
var classfile = ClassFile.of();
42+
transformedClassBytes = classfile.transformClass(
43+
classfile.parse(classBytes),
44+
ClassTransform.dropping(ce -> ce instanceof MethodModel mm && mm.methodName().stringValue().startsWith("set"))
45+
);
46+
return transformedClassBytes;
47+
}
48+
49+
static byte[] transformMethod(byte[] classBytes) {
50+
System.out.println("Transforming timestamp getter");
51+
ClassModel originalClassModel = ClassFile.of().parse(classBytes);
52+
byte[] transformedClassBytes = ClassFile.of().build(
53+
originalClassModel.thisClass().asSymbol(),
54+
cb -> {
55+
for (ClassElement ce : originalClassModel) {
56+
// replaces the getTimestamp method
57+
if (ce instanceof MethodModel mm && mm.methodName().stringValue().equals("getTimestamp")) {
58+
var mtdInt = MethodTypeDesc.of(ConstantDescs.CD_int);
59+
cb.withFlags()
60+
.withMethodBody(
61+
mm.methodName().stringValue(), mtdInt, ClassFile.ACC_PUBLIC,
62+
cob -> cob.ldc(42).ireturn()
63+
);
64+
} else {
65+
cb.with(ce);
66+
}
67+
}
68+
}
69+
);
70+
return transformedClassBytes;
71+
}
72+
73+
static void writeFile(byte[] bytes) {
74+
System.out.println("Writing BasicClass file");
75+
try {
76+
Files.write(Paths.get("BasicClass.class"), bytes);
77+
} catch (Exception ex) {
78+
System.err.println("Error during writing: " + ex.getMessage());
79+
}
80+
}
81+
}

java-24/ClassFileApiWritingExample.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import java.nio.file.OpenOption;
66

77
/**
8-
* Run: `java --source 24 --enable-preview ClassFileApiWritingExample.java`
8+
* Run: `java ClassFileApiWritingExample.java`
99
*/
1010
public class ClassFileApiWritingExample {
1111
public static void main(String[] args) {

0 commit comments

Comments
 (0)