Generic Bounding
توضیحات جلسه
جزوه و مستندات
مفاهیم کلیدی
- Bounding مرزبندی
فرآیندی در Generics است که به کمک آن میتوان محدوده تایپهای ورودی را کنترل کرد. این کار باعث میشود علاوه بر حفظ Type Safety، انعطافپذیری کد نیز افزایش پیدا کند.
- Upper Bounded Wildcard
با استفاده از عبارت ? extends T تعریف میشود. این ساختار یعنی تایپ موردنظر میتواند خود کلاس T یا هر کلاسی باشد که از آن ارثبری کرده است؛ یعنی Subclasses.
- Lower Bounded Wildcard
با استفاده از عبارت ? super T تعریف میشود. این ساختار یعنی تایپ موردنظر میتواند خود کلاس T یا هرکدام از کلاسهای والد آن، یعنی Superclasses، تا سطح Object باشد.
- Wildcard
علامت سؤال ? نشاندهنده یک تایپ ناشناخته یا Unknown Type است. زمانی که با Bounding ترکیب میشود، اجازه میدهد این تایپ ناشناخته در یک بازه مشخص از سلسلهمراتب وراثت محدود شود.
- Type Parameter Bounding
به معنای اعمال محدودیت روی پارامترهای جنریک در سطح تعریف کلاس یا Class است. برای مثال، میتوان یک پارامتر جنریک را طوری محدود کرد که فقط انواع عددی را بپذیرد.
موارد مصاحبه ای
- تفاوت اصلی بین Upper Bound و Lower Bound در بحث Read و Write چیست؟
در Upper Bound، یعنی استفاده از extends، خواندن دادهها یا Get بسیار امن است؛ چون مطمئن هستیم خروجی حتماً از نوع کلاس پایه است. اما اضافه کردن داده یا Add محدودیت جدی دارد.
در Lower Bound، یعنی استفاده از super، اضافه کردن داده بسیار راحت و امن است. اما هنگام خواندن، کامپایلر نوع دقیق را نمیداند و خروجی را Object در نظر میگیرد.
- چرا در ساختار
? extends Tنمیتوانیم عضو جدیدی به لیست اضافه کنیم؟
چون کامپایلر دقیقاً نمیداند لیست از چه زیرمجموعهای از T ساخته شده است. برای مثال، اگر لیستی از Animal داشته باشیم که در واقع لیستی از Cat است، اضافه کردن یک Dog به آن باعث بروز خطا در زمان اجرا میشود.
جاوا برای جلوگیری از این ریسک، اجازه Add کردن هیچ مقداری، بهجز null، را نمیدهد.
- چه زمانی از
T extends Numberدر تعریف یک کلاس استفاده میکنیم؟
زمانی که میخواهیم پارامتر جنریک کلاس فقط شامل انواع عددی مانند Integer، Double و Long باشد و از ورود تایپهای غیرمرتبط مانند String یا کلاسهای سفارشی دیگر جلوگیری کنیم.
- خروجی متد
getدر ساختار Lower Bound چیست و چه چالشی دارد؟
خروجی همیشه از نوع Object است. چالش اصلی این است که برای استفاده از متدهای اختصاصی آن کلاس، باید عملیات Casting را بهصورت صریح یا Explicit انجام دهیم.
سناریو کاربردی
در ویدیو دو سناریوی مهم بررسی شده است:
۱. مدیریت رفتار حیوانات
فرض کنید کلاسی به نام Animal داریم که متد sound() دارد و کلاسهای Dog و Cat از آن ارثبری کردهاند.
اگر بخواهیم متدی بنویسیم که لیستی از حیوانات را بگیرد و صدای آنها را پخش کند، از ? extends Animal استفاده میکنیم. این کار به ما اجازه میدهد هم لیستی از سگها و هم لیستی از گربهها را به متد پاس دهیم و با خیال راحت متد sound() را روی تکتک آنها اجرا کنیم؛ چون مطمئن هستیم هر چیزی که در لیست قرار دارد، حتماً یک Animal است.
۲. کلاس Person با آیدی جنریک
در طراحی کلاس Person ممکن است بخواهیم نوع ID را منعطف نگه داریم؛ برای مثال، گاهی Integer و گاهی Long باشد.
با استفاده از <T extends Number> در تعریف کلاس، مرزبندی میکنیم تا فقط انواع عددی بهعنوان آیدی پذیرفته شوند. اگر کسی تلاش کند کلاسی مانند Animal را بهعنوان نوع آیدی پاس دهد، کامپایلر همان لحظه خطا میدهد.
بیشتر بدانید
- نکته در مورد Add
در حالت Upper Bound، تنها مقداری که مجاز به اضافه کردن به لیست هستید، مقدار null است.
- امنیت در خواندن
استفاده از Upper Bound برای متدهایی که فقط وظیفه پردازش و خواندن اطلاعات از یک ساختار داده را دارند، یعنی حالت ReadOnly، بهترین گزینه است.
- انعطاف در نوشتن
اگر متدی طراحی میکنید که وظیفه آن جمعآوری و ذخیره اشیا در یک لیست است، استفاده از Lower Bound انعطافپذیری بسیار خوبی به کد شما میدهد.
