ถาม javac เอง ว่าอะไรใช้ได้แล้วจริง
คอร์สนี้ไม่ได้เริ่มด้วยรายการ feature ใหม่
มันเริ่มด้วยปัญหาที่มาก่อนรายการนั้น — คุณจะรู้ได้อย่างไรว่าของที่อ่านเจอ ยังใช้ได้ในรุ่นที่คุณรันอยู่
ทุกตัวอย่างแปลด้วย javac และรันด้วย java ตรงๆ · ไม่มี Maven ไม่มี Gradle
เหตุผลมีสองข้อ · ข้อแรกคือคอร์สนี้พูดเรื่องภาษา ไม่ใช่เรื่องเครื่องมือ · ข้อที่สองสำคัญกว่า คือคุณคัดลอกตัวอย่างไปวางแล้วรันได้ทันทีโดยไม่ต้องเตรียมอะไร
ของที่ต้องมีอย่างเดียวคือ JDK 25 · ตรวจด้วย javac --version
API ที่บทความสามปีหลังสอนไว้ แปลไม่ผ่านแล้ว
หัวข้อที่มีชื่อว่า “API ที่บทความสามปีหลังสอนไว้ แปลไม่ผ่านแล้ว”เริ่มจากตัวอย่างที่เจ็บที่สุด เพราะมันเป็นของที่คนลอกไปใช้บ่อยที่สุด
ค้นเรื่อง structured concurrency ของ Java แล้วคุณจะเจอรูปนี้ในเกือบทุกบทความ
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { scope.fork(() -> 1); scope.join().throwIfFailed();}แปลบน JDK 25 แล้วได้
error: cannot find symbolไม่ใช่คำเตือน ไม่ใช่ deprecation · ชื่อนั้นไม่มีอยู่แล้ว
วิธีถาม compiler เอง
หัวข้อที่มีชื่อว่า “วิธีถาม compiler เอง”สองคำสั่ง ต่างกันที่ธงเดียว · ผลของทั้งคู่รวมกันบอกสถานะได้ครบ
เขียนตัวอย่างสั้นที่สุดที่ใช้ feature นั้น แล้วแปลสองรอบ
javac --release 25 F.javajavac --release 25 --enable-preview F.javaอ่านผลตามนี้
| รอบแรก | รอบที่สอง | แปลว่า |
|---|---|---|
| ผ่าน | — | final · ใช้ได้เลย ไม่ต้องใส่ธง |
| ไม่ผ่าน | ผ่าน | preview · ใช้ได้แต่มีราคา |
| ไม่ผ่าน | ไม่ผ่าน | ไม่มีในรุ่นนี้ |
—release สำคัญกว่าที่หน้าตาบอก · มันจำกัด API ให้ตรงรุ่นด้วย ไม่ใช่แค่รูปของภาษา
· ต่างจาก -source กับ -target ที่จำกัดแค่รูป แล้วปล่อยให้คุณเรียก API ที่รุ่นเป้าหมายไม่มี
flowchart TB
S["ตัวอย่างสั้นที่สุดที่ใช้ feature นั้น"]
A{"javac --release 25"}
B{"javac --release 25<br/>--enable-preview"}
S --> A
A -->|"ผ่าน"| F["final"]
A -->|"ไม่ผ่าน"| B
B -->|"ผ่าน"| P["preview"]
B -->|"ไม่ผ่าน"| N["ไม่มีในรุ่นนี้"]
คำบรรยายภาพ: คำถามเดียวกันสองรอบ ต่างกันที่ธงเดียว แล้วอ่านสถานะจากผล
ผลจริงของสิบ feature บน javac 25
หัวข้อที่มีชื่อว่า “ผลจริงของสิบ feature บน javac 25”แปดข้อแรกรันได้โดยไม่ต้องใส่ธงอะไรเลย
| feature | สถานะ | รันแล้วพิมพ์อะไรออกมา |
|---|---|---|
record | FINAL | P[x=1, y=2] |
sealed + switch | FINAL | A |
| record pattern | FINAL | 10 |
| text block | FINAL | hello |
Gatherers | FINAL | [[1, 2], [3, 4]] |
| virtual thread | FINAL | true |
ScopedValue | FINAL | x |
import module | FINAL | [x] |
StructuredTaskScope | PREVIEW | 1 |
| ชนิดพื้นฐานใน pattern | PREVIEW | int 1 |
แปด final สอง preview · และสองตัวที่เป็น preview ต้องใส่ —enable-preview ทั้งตอนแปลและตอนรัน · ใส่ที่เดียวแล้วอีกที่จะปฏิเสธ
คิดว่ายัง preview ทั้งที่ final แล้ว · Gatherers กับ ScopedValue เข้ารุ่นถาวรแล้ว
· คนที่ยังจำว่าเป็น preview จะเลี่ยงมันโดยไม่จำเป็น แล้วเขียน code ที่ยาวกว่าที่ควร
คิดว่า stable ทั้งที่ยัง preview · StructuredTaskScope ยังเป็น preview featurepreview featurefeature ที่เข้ารุ่นแล้วแต่ยังเปลี่ยนรูปได้ · ต้องเปิดธงทั้งตอนแปลและตอนรัน และ class file ที่ได้ถูกตรึงกับรุ่นที่แปลมัน
· คนที่คิดว่ามันนิ่งแล้วจะเอาไปใช้ในของที่ส่งขึ้น production แล้วเจอว่ารุ่นหน้าเปลี่ยนรูป
· บทที่ 7 วัดราคาของข้อนี้ออกมาเป็นข้อความที่ JVM ตอบ
ทางแก้ของทั้งสองข้อคือคำสั่งเดียวกัน · แปลสองรอบแล้วอ่านผล ใช้เวลาน้อยกว่าการค้นหา
รูปของ structured concurrency ที่ใช้ได้ใน 25
หัวข้อที่มีชื่อว่า “รูปของ structured concurrency ที่ใช้ได้ใน 25”รูปนี้แปลผ่านและรันได้ เมื่อใส่ธง
try (var scope = StructuredTaskScope.open()) { var s = scope.fork(() -> 1); scope.join(); System.out.println(s.get());}ต่างจากรูปเก่าสามอย่าง คือไม่มี new · ไม่มีชนิดย่อยชื่อ ShutdownOnFailure
· และ join() ไม่คืนของที่มี throwIfFailed
นี่คือเหตุผลที่บทนี้เป็นบทแรก ไม่ใช่เพราะสถานะสำคัญกว่าเนื้อหา · แต่เพราะการเดินต่อโดยไม่รู้สถานะ ทำให้ทุกบทที่เหลืออ่านไม่รู้เรื่อง
สิ่งที่บทนี้ยังไม่ทำ
หัวข้อที่มีชื่อว่า “สิ่งที่บทนี้ยังไม่ทำ”สามอย่างที่ยังไม่มี และแต่ละอย่างมาถึงเมื่อมีเหตุผลให้ต้องมี
- ยังไม่ได้บอกว่าแต่ละ feature ให้อะไรกลับมา บทนี้ตอบแค่ว่าใช้ได้ไหม · บทที่ 2 ถึง 6 ตอบว่าได้อะไร
- ยังไม่ได้วัดราคาของ preview รู้แค่ว่าต้องใส่ธง · บทที่ 7 วัดว่ามันตรึงอะไรไว้บ้าง
- ยังไม่ได้แตะเรื่องการอ่าน bytecode ซึ่งเป็นเครื่องมือหลักของบทที่ 2 กับ 4
สรุปก่อนไปต่อ
หัวข้อที่มีชื่อว่า “สรุปก่อนไปต่อ”บทนี้ให้เครื่องมือหนึ่งอย่าง และมันใช้ได้กับรุ่นถัดไปที่ยังไม่ออกด้วย
คุณตอบเองได้แล้วว่า feature ไหนใช้ได้ในรุ่นที่คุณรันอยู่ โดยไม่ต้องเชื่อบทความไหน · วิธีนี้ไม่ล้าสมัย เพราะมันถาม compiler ที่อยู่บนเครื่องคุณ ไม่ใช่ถามคนที่เขียนเมื่อสามปีก่อน
บทหน้าเราเปิดของชิ้นแรก — record · และคำถามไม่ใช่ว่ามันพิมพ์สั้นลงแค่ไหน
แต่เป็นว่า javap เห็นอะไรที่ source ไม่ได้บอก
ทุกแถวในตารางมาจากการแปลและรันจริง ไม่ได้คัดมาจากเอกสาร:
- วัดเมื่อ 2026-09-01 บน macOS arm64 ·
javac 25.0.4.1·openjdk 25.0.4.1 2026-08-18(Homebrew) - รันซ้ำเองได้ด้วย
npm run verify:java-modernซึ่งแปลตัวอย่างสิบอันสองรอบ แล้วรันทุกอันที่แปลผ่าน · หา JDK 25 ไม่เจอ มันออกด้วยรหัส 2 ซึ่งแปลว่าวัดไม่ได้ ไม่ใช่ว่าข้ออ้างผิด - JDK 25 release notes (เข้าถึง 2026-09-01) — รายชื่อ JEP ที่เข้ารุ่นนี้
- javac — standard options
(เข้าถึง 2026-09-01) —
--releaseกับความต่างจาก-sourceและ-target
เช็กความเข้าใจ — บทที่ 1
ข้อ 1 / 3แปลตัวอย่างด้วย `javac --release 25` แล้วไม่ผ่าน · แต่ใส่ `--enable-preview` แล้วผ่าน · แปลว่าอะไร