JEP 401 (اشیاء مقداری / Value Objects) در JDK 28 فعال شده و رفتار عملگر == را برای کلاس‌هایی که با اصلاح‌کننده value تعریف می‌شوند بازتعریف می‌کند. این مدل نمونه‌هایی بدون هویت و با فیلدهای ضمنیِ نهایی معرفی می‌کند و درهای بهینه‌سازی‌هایی مانند اسکالرایز و فِلَت کردن (flattening) را در JVM می‌گشاید. برای آزمایش باید گزینه --enable-preview هنگام کامپایل و اجرا فعال باشد.

چه چیزی تغییر می‌کند؟

کلاس‌هایی که با value اعلام شوند به «کلاسِ مقداری» تبدیل می‌شوند؛ سایر کلاس‌ها همچنان «کلاس هویتی» باقی می‌مانند. نکات کلیدی مدل جدید:

  • فیلدهای نمونه به‌صورت ضمنی نهایی هستند و هر فیلد باید پیش از قابل‌مشاهده شدن نمونه مقداردهی شود. (الزام بایت‌کدی توسط JEP 539 تعیین می‌شود)
  • متدهای نمونهٔ یک کلاس مقداری نباید synchronized باشند.
  • رکوردها نیز می‌توانند با اصلاح‌کننده value تعریف شوند، برای مثال: value record Color(byte red, byte green, byte blue) { }.
  • عملگر == برای اشیاء مقداری بر مبنای مقایسهٔ ساختاری فیلدها تعریف شده است؛ برای فیلدهای مرجع، مقایسه به‌طور بازگشتی با == انجام می‌شود.

مثال ساده

value class Point {
    private int x; // ضمنی نهایی
    private int y;
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
    public int x() { return x; }
    public int y() { return y; }
}

Point p1 = new Point(3, 4);
Point p2 = new Point(3, 4);
assert p1 == p2; // true: همان کلاس و مقادیر فیلد برابر

مقایسهٔ == و equals

بازتعریف == به معنای کنار گذاشتن equals نیست. هنوز برای مقایسهٔ مفهومی و قراردادهای دامنه توصیه می‌شود از equals استفاده شود، زیرا وضعیت داخلی یک شیء مقداری همیشه نمایانگر معنای دامنه‌ای آن نیست.

تأثیر بر کتابخانه‌ها و مسیر مهاجرت

با فعال شدن پیش‌نمایش، برخی کلاس‌های مبتنی بر مقدار در JDK مانند بسته‌بندی‌کننده‌های نوع‌های اولیه و LocalDate به کلاس مقداری تبدیل می‌شوند؛ در غیر این صورت رفتار همانند JDK 27 خواهد بود. نکات مهم مهاجرت:

  • کدی که با پیش‌نمایش فعال کامپایل شده باید با همان پیش‌نمایش اجرا شود.
  • هنگام مهاجرت یک کلاس، صفت LoadableDescriptors در فایل کلاس به JVM نشان می‌دهد که آن کلاس مقداری است؛ بازکامپایل توصیه می‌شود تا ناسازگاری‌ها کاهش یابد.
  • برخی APIها ممکن است بی‌اثر شوند؛ برای مثال تلاش برای ایجاد یک Reference به یک شیء مقداری می‌تواند منجر به پرتاب IdentityException شود و javac هشدارهای هویت را گسترش می‌دهد.
نمونه‌ای از کد جاوا با کلاس مقداری Point

مزایا و محدودیت‌های اجرایی

هدف اصلی بهبود کارایی و کاهش هزینهٔ مدیریت هویت است، نه تضمین افزایش آنی در بنچمارک‌ها. JVM می‌تواند از دو مسیر بهینه‌سازی استفاده کند:

  • اسکالرایز کردن (scalarize): تبدیل یک شیء مقداری به فیلدهای منفرد در زمان اجرا.
  • فِلَت کردن (flattening): ذخیرهٔ فشردهٔ شیء مستقیماً در یک فیلد یا عنصر آرایه؛ این روش به محدودیت‌های اتمیک و نحوهٔ نمایش وابسته است.

اگر هیچ‌یک از این بهینه‌سازی‌ها اعمال نشود یا در دورهٔ گرم‌شدن JVM، رفتار به تخصیص معمولی بازگردد.

خطرات امنیتی و رفتاری

سند JEP دو نگرانی کلیدی را برجسته می‌کند: تغییر در == و identityHashCode می‌تواند به افشای غیرمستقیم اطلاعات خصوصی منجر شود؛ همچنین مقایسهٔ بازگشتیِ بزرگ بین دو ساختار مقداری ممکن است زمان اجرای نامطلوب یا قابل‌استفاده برای حملات تایمینگ ایجاد کند. علاوه بر این، غیرفعال شدن synchronized در متدهای نمونه و تغییر در مفاهیم هویتی نیاز به دقت دارد.

راهنمای عملی برای توسعه‌دهندگان

  • پیش از وارد کردن پیش‌نمایش به محیط‌های حیاتی، تمام کدهای حساس را با --enable-preview آزمایش و سپس بازکامپایل کنید.
  • تست‌های همزمانی را بازبینی کنید؛ متدهای synchronized در کلاس‌های مقداری مجاز نیستند و رفتار رقابت ممکن است تغییر کند.
  • هنگام طراحی انواع ساده (رنگ، نقطه، تاریخ) مطمئن شوید فقدان هویت با مدل دامنه‌تان سازگار است.
  • برای کتابخانه‌ها و APIهای عمومی، سناریوهای سازگاری عقب‌به‌عقب را مدنظر قرار دهید و مواردی مانند Referenceها و هش‌های هویتی را بررسی کنید.

منابع

صفحات رسمی و مستندات مرتبط: Valhalla، JEP 401، JEP 539. مروری کلی بر زبان جاوا در ویکی‌پدیا نیز مفید است.

JEP 401 یکی از تحولات ساختاری مهم در جاوا است که امکان کاهش تخصیص‌ها و بهبود کارایی حافظه را فراهم می‌کند؛ با این حال، برای پیشگیری از مشکلات زمان اجرا نیاز به آزمایش، بازکامپایل و آگاهی از تغییرات رفتاری وجود دارد.