ข้าม​ไป​ยัง​เนื้อหา

ถาม javac เอง ว่า​อะไร​ใช้ได้​แล้ว​จริง

คอร์ส​นี้​ไม่​ได้​เริ่ม​ด้วย​รายการ feature ใหม่

มัน​เริ่ม​ด้วย​ปัญหา​ที่มา​ก่อน​รายการ​นั้น — คุณ​จะ​รู้​ได้​อย่างไร​ว่า​ของ​ที่​อ่าน​เจอ ยัง​ใช้ได้​ใน​รุ่น​ที่​คุณ​รัน​อยู่

📦 ทั้ง​คอร์ส​นี้​ไม่มี build tool

ทุก​ตัวอย่าง​แปล​ด้วย 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 · ชื่อ​นั้น​ไม่มี​อยู่​แล้ว

สอง​คำ​สั่ง ต่าง​กัน​ที่​ธง​เดียว · ผล​ของ​ทั้ง​คู่​รวม​กัน​บอก​สถานะ​ได้​ครบ

เขียน​ตัวอย่าง​สั้น​ที่สุด​ที่​ใช้ feature นั้น แล้ว​แปล​สอง​รอบ

Terminal window
javac --release 25 F.java
javac --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สถานะรัน​แล้ว​พิมพ์​อะไร​ออก​มา
recordFINALP[x=1, y=2]
sealed + switchFINALA
record patternFINAL10
text blockFINALhello
GatherersFINAL[[1, 2], [3, 4]]
virtual threadFINALtrue
ScopedValueFINALx
import moduleFINAL[x]
StructuredTaskScopePREVIEW1
ชนิด​พื้น​ฐาน​ใน patternPREVIEWint 1

แปด final สอง preview · และ​สอง​ตัว​ที่​เป็น preview ต้อง​ใส่ —enable-preview ทั้ง​ตอน​แปล​และ​ตอน​รัน · ใส่​ที่​เดียว​แล้ว​อีก​ที่​จะ​ปฏิเสธ

คน​คิด​ผิด​ได้​สอง​ทาง และ​ทั้ง​สอง​ทาง​มี​ค่า​ใช้​จ่าย

คิด​ว่า​ยัง preview ทั้ง​ที่ final แล้ว · Gatherers กับ ScopedValue เข้า​รุ่น​ถาวร​แล้ว · คน​ที่​ยัง​จำ​ว่า​เป็น preview จะ​เลี่ยง​มัน​โดย​ไม่​จำเป็น แล้ว​เขียน code ที่​ยาว​กว่า​ที่​ควร

คิด​ว่า stable ทั้ง​ที่​ยัง preview · StructuredTaskScope ยัง​เป็น preview featurepreview featurefeature ที่​เข้า​รุ่น​แล้ว​แต่​ยัง​เปลี่ยน​รูป​ได้ · ต้อง​เปิด​ธง​ทั้ง​ตอน​แปล​และ​ตอน​รัน และ class file ที่​ได้​ถูก​ตรึง​กับ​รุ่น​ที่​แปล​มัน · คน​ที่​คิด​ว่า​มัน​นิ่ง​แล้ว​จะ​เอา​ไป​ใช้​ใน​ของ​ที่​ส่ง​ขึ้น production แล้ว​เจอ​ว่า​รุ่น​หน้า​เปลี่ยน​รูป · บท​ที่ 7 วัด​ราคา​ของ​ข้อ​นี้​ออก​มา​เป็น​ข้อความ​ที่ JVM ตอบ

ทาง​แก้​ของ​ทั้ง​สอง​ข้อ​คือ​คำ​สั่ง​เดียวกัน · แปล​สอง​รอบ​แล้ว​อ่าน​ผล ใช้​เวลา​น้อย​กว่า​การ​ค้นหา

รูป​นี้​แปล​ผ่าน​และ​รัน​ได้ เมื่อ​ใส่​ธง

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` แล้วผ่าน · แปลว่าอะไร