دانستنی‌ها

کلاس‌های‌مهر و موم‌شده در جاوا

در این مطلب با قابلیت جدید Sealed Class در جاوا ۱۵ آشنا می‌شویم.

هدف اصلی از وراثت در جاوا، استفاده دوباره از کد (code reuse) است. با این امکان، کلاس‌های متعدد می‌توانند از کلاس دیگر ارث‌بری کنند و از متد‌های از قبل تعریف‌شده آن برای خودشان استفاده کنند یا آن‌‌ها را گسترش دهند. هدف مهم دیگر (که به هدف اول هم چندان بی‌ربط نیست) این است که مدل و سلسله‌مراتبی برای درک بهتر پروژه طراحی شود. حالا با امکان «کلاس‌های مهر و موم‌شده» که به نسخه ۱۵ جاوا اضافه شده است، با محدود کردن این‌که چه کلاسی می‌تواند از کلاس ما ارث‌بری کند، طراحی این مدل کامل‌تر و پر معناتر می‌شود.

امکانات نسخه‌های پیشین برای کنترل ارث‌بری

با ابزارهای نسخه‌های پیشین جاوا هم می‌توان ارث‌بری را محدود کرد. مثلا می‌توانید کلاس مورد نظر را با دسترسی package یا private تعریف کرد تا فقط کلاس‌هایی که خودمان تعریف می‌کنیم بتوانند از آن ارث‌بری کنند. در این حالت سوپرکلاس را package access یا private تعریف کرده و کلاس‌های فرزند را به صورت public تعریف می‌کنند.

مثالی از این حالت، کلاس AbstractStringBuilder است که در کتابخانه استاندارد تعریف شده تا سوپرکلاسی برای StringBuilder و StringBuffer باشد و متد append را برای هر دوی آن‌ها به ارث گذاشته تا از آن استفاده کنند.

package java.lang;

abstract class AbstractStringBuilder {...}
public final class StringBuffer  extends AbstractStringBuilder {...}
public final class StringBuilder extends AbstractStringBuilder {...}

همان‌طور که می‌بینید، وقتی هدفمان استفاده دوباره از کد باشد، این روش به کارمان می‌آید ولی با این کار معنای انتزاع و همین‌طور امکانات چند ریختی را از دست می‌دهیم. همچنین نمی‌توانیم دیگر از اشیا سوپرکلاس استفاده کرده و روی نوع آن switch بزنیم. (این امکان در نسخه‌های آینده جاوا اضافه می‌شود و در ادامه همین مطلب معرفی می‌شود.)

یک راه دیگر برای محدود کردن ارث‌بری می‌تواند این باشد که کلاس را final تعریف کنیم که در این حالت به هیچ صورت نمی‌توانیم از کلاس ارث بری کنیم.

راه حل: کلاس‌های مهر و موم‌شده

از جاوا 15 به بعد می‌توانیم کلاس‌های مهر و موم‌شده (Sealed) تعریف کنیم. این نوع از کلاس‌ها نیازی که در بالا گفته شد را بر طرف می‌کنند. با یک مثال می‌خواهیم این نیاز را به طور دقیق‌تر مطرح کنیم و کاربرد کلاس‌های مهر و موم‌شده را در حل آن توضیح دهیم.

فرض کنید یک کلاس وسیله نقلیه (Vehicle) داریم. می‌خواهیم برای آن دو زیر کلاس وسیله نقلیه موتوری (MotorizedVehicle) و غیرموتوری (NonMotorizedVehicle) تعریف کنیم و در عین حال اجازه ندهیم هیچ کلاس دیگری از آن ارث‌بری کند. چون غیر از این هیچ دسته‌بندی دیگری برای وسایل نقلیه نداریم و این محدودیت ارث‌بری معنای مدل را کامل‌تر می‌کند. برای تعریف کلاس مهر و موم‌شده از کلمه کلیدی sealed استفاده می‌کنیم.

public sealed abstract class Vehicle permits MotorizedVehicle, NonMotorizedVehicle{ … }

کلاس‌های مهر و موم‌شده باید زیرکلاس داشته باشند و گرنه خطای کامپایل به وجود می‌آید. زیرکلاس‌های آ‌ن ‌را بعد از کلمه کلیدی permits لیست می‌کنیم. اگر این کلاس‌ها در یک پکیج نباشند، باید نام کامل آن‌ها (مثل com.example.MotorizedVehicle) را بیاورید. حال دو کلاس MotorizedVehicle و NonMotorizedVehicle را تعریف می‌کنیم. اگر کلاسی از یک sealed کلاس ارث‌بری کند حتما باید یکی از سه کلمه کلیدی final یا sealed یا non-sealed را در امضای خود استفاده کند. اگر از کلمه کلیدی non-sealed استفاده کنیم دیگر محدودیتی برای ارث‌بری از آن کلاس وجود ندارد.

کلاس NonMotorizedVehicle را هم به صورت sealed تعریف می‌کنیم. چون نمی‌خواهیم از آن هم ارث‌بری دیگری انجام شود.

public sealed abstract class NonMotorizedVehicle extends Vehicle permits Sleigh , Bicycle{ ... }

برای سادگی، دو زیرکلاس Bicycle و Sleigh برای NonMotorizedVehicle تعریف می‌کنیم و هر دو را به صورت final قرار می‌دهیم.

public final class Sleigh extends NonMotorizedVehicle  { ... }
public final class Bicycle extends NonMotorizedVehicle { ... }

حالا دیگر از زیر شاخه NonMotorizedVehicle نمی‌توانیم هیچ‌گونه ارث بری‌ای انجام دهیم.

اکنون کلاس MotorizedVehicle را به گونه‌ای تعریف می‌کنیم که فقط دو زیر کلاس ماشین و کامیون داشته‌باشد.

public sealed class MotorizedVehicle extends Vehicle permits Truck, Car{ ...}

چون کلاس Car می‌تواند انواع بسیار مختلفی داشته باشد, می‌خواهیم آنرا non-sealed تعریف کنیم تا کاربر بتواند از آن ارث‌بری کند و بر حسب نیاز، ویژگی‌های مورد نیاز خود را به آن اضافه کند:

public non-sealed class  Car extends MotorizedVehicle{ ...}

یادتان باشد در این حالت کاربر فقط از کلاس Car می‌تواند ارث‌بری کند و نه کلاس MotorizedVehicle.

کلاس Truck را هم برای سادگی دوباره final تعریف می‌کنیم تا کار تمام شود.

public final class Truck extends MotorizedVehicle{ ... }

سلسله مراتب کلی این کلاس‌ها را در این شکل می‌توانید ببینید:

سلسله مراتب کلاس‌های این مثال

همچنین لازم به ذکر است که معنی sealed در #C همان final در جاوا است و نباید با آن اشتباه گرفته شود.

کلمه کلیدی sealed برای واسط‌ها هم به کار می‌رود. فقط به یاد داشته باشید اگر واسطی مهر و موم‌شده باشد، هم واسط‌هایی که از آن ارث‌بری می‌کنند را محدود می‌کند و هم کلاس‌هایی که آن‌را پیاده‌سازی می‌کنند.

در پی این تغییر، متدهای زیر به کلاس Class اضافه شده‌اند:

  • ()boolean isSealed: اگر کلاس به صورت مهر و موم‌شده تعریف‌شده باشد، true برمی‌گرداند در غیر این صورت false.
  • ()java.lang.constant.ClassDesc[] getPermittedSubclasses: آرایه‌ای از اشیا ClassDesc را برمی‌گرداند که نشان‌دهنده همه اشیایی هستند که که این کلاس به آ‌ن‌ها اجازه ارث‌بری داده ‌است. اگر کلاس sealed نباشد، null برمی‌گداند.

سخن آخر

کلاس‌های مهر و موم‌شده مهم‌ترین تغییر جاوا 15 بوده و امکانات شی‌گرایی جاوا را بسیار بهتر کرده‌ است. برای دیدن توضیح رسمی آن به JEP 360 مراجعه کنید. با اضافه شدن این امکان به جرأت می‌توان گفت جاوا شی‌گراترین زبان برنامه‌نویسی موجود است. برای تکمیل این امکان قرار است امکان switch زدن روی نوع کلاس اشیا هم به جاوا اضافه شود. البته قول این امکان به همراه pattern matching مدت زیادی است که به برنامه‌نویس‌ها داده شده ولی هنوز به صورت رسمی به جاوا اضافه نشده‌اند.

.

.

.


با ما همراه باشید

کانال تلگرام: JavaCupIR@

اکانت توییتر: JavaCupIR@

صفحه اینستاگرام: javacup.ir

صفحه ویرگول: javcup

گروه لینکدین: Iranian Java Developers

نوشته های مشابه

‫2 دیدگاه ها

  1. چقدر پشیمونم از انتخاب جاوا
    کاش یه فرصتی بشه برم گو کار کنم از دست همه این جنگولک یاز هاش راحت شم
    حالا جاوا قراره پروژه لوم رو راه بندازه که یعنی خداحافظ reactive و concurrent

  2. سرعت پیشرفت جاوا خنده داره. واقعا درک نمیکنم اوراکل هدفش چیه. خیلی از امکانات مهم و اصلی رو خیلی دیر پیاده سازی میکنه یا اصلا بیخیالش میشه (مثل method overloading). یا مثلا همین pattern matching که گفتید خیلی وقته زبانای دیگه اضافش کردن.

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا