นากกายสีมิ้นต์ต้นฉบับชี้วงกลมและสี่เหลี่ยมให้ไหลลงกล่องรูปทรงที่ตรงกัน

Java Pattern Matching for instanceof: ตรวจชนิดและผูกตัวแปรโดยไม่ cast ซ้ำ

น้องกายEngineering

โค้ด Java แบบเดิมมักตรวจ value instanceof String แล้ว cast (String) value ในบรรทัดถัดไป การทำสองครั้งเปิดช่องให้ชนิดในเงื่อนไขกับ cast ไม่ตรงกันหลัง refactor Pattern matching ให้เขียน type pattern ครั้งเดียวและสร้างตัวแปรที่ใช้ได้เฉพาะทางที่ compiler พิสูจน์ว่าตรงชนิด

HOW: flow scoping ตามตรรกะของเงื่อนไข

ตัวแปร pattern ไม่ได้มี scope ครอบทั้ง method มันมีเฉพาะจุดที่ match แน่นอน จึงใช้ร่วมกับ && ได้ เพราะฝั่งขวารันเมื่อฝั่งซ้ายจริง แต่ใช้กับ || แล้วอ้างตัวแปรฝั่งขวาไม่ได้ เนื่องจากฝั่งซ้ายอาจ false และตัวแปรไม่ถูกสร้าง

public class Main {
    static String normalize(Object value) {
        if (value instanceof String text && !text.isBlank()) {
            return text.trim().toLowerCase();
        }
        if (value instanceof Integer number && number >= 0) {
            return Integer.toString(number);
        }
        return "unsupported";
    }

    static int lengthOrZero(Object value) {
        if (!(value instanceof String text)) {
            return 0;
        }
        return text.length();
    }

    public static void main(String[] args) {
        assert normalize("  HELLO ").equals("hello");
        assert normalize(7).equals("7");
        assert normalize(-1).equals("unsupported");
        assert normalize(null).equals("unsupported");
        assert lengthOrZero("abc") == 3;
        assert lengthOrZero(12) == 0;
    }
}

ตัวอย่าง lengthOrZero แสดง flow scoping หลัง early return เมื่อเงื่อนไขปฏิเสธชนิด compiler รู้ว่าทางที่เหลือ text ต้องมีค่า การเขียน guard clause แบบนี้ลด nesting และยัง type-safe

จุดที่ pattern ไม่ช่วยแก้

ถ้า method มี instanceof ยาวสิบกิ่ง ปัญหาอาจเป็น abstraction ผิด ไม่ใช่ syntax ยาว สำหรับพฤติกรรมที่เป็นของแต่ละชนิด polymorphism หรือ sealed hierarchy มักชัดกว่า ส่วน input จาก JSON ควรถูก validate ก่อนถึง domain; การแตกกิ่งตาม runtime type ไม่แทน schema validation

Pattern instanceof จัดการ null อย่างปลอดภัยด้วยผล false แต่การรับ null อาจยังผิดสัญญาของ API อย่าปล่อยให้ syntax ที่สะดวกซ่อน invariant ควรใช้ Objects.requireNonNull ที่ boundary หาก null ไม่ใช่ค่าที่อนุญาต

Recap

ใช้ type pattern เพื่อลบ cast ซ้ำ อาศัย && และ guard clause ให้ flow scope ชัด และหยุดเมื่อ branch มากจนควรปรับ model งานวันนี้คือให้ IDE หา instanceof ตามด้วย cast ชนิดเดิม แล้ว refactor พร้อม test null และชนิดที่ไม่รองรับครับ