آموزش قدم به قدم جاوا – قسمت بیست و چهارم – بخش اول

مفهوم جنریک

جنریک‌ها به ما این امکان را می‌دهند تا کلاس‌ها، اینترفیس‌ها و متدهایی تعریف کنیم و نوع داده‌ای که با آن کار می‌کنند را به عنوان یک پارامتر به آن‌ها بدهیم. کدهای جنریک به صورت خودکار با نوع داده‌ای که به آن‌ها داده می‌شود کار می‌کنند بدون اینکه نیاز به تغییر کد باشد. بسیاری از کلاس‌ها و متدها می‌توانند با نوع داده‌های مختلف کار کنند بدون آنکه نیاز به تغییر کد باشد.

نحوه استفاده از یک کلاس جنریک

کلاسی به نام ArrayList در پکیج java.util قرار دارد. با استفاده از این کلاس می‌توانیم یک آرایه داینامیک (آرایه‌ای که اندازه آن می‌تواند در زمان اجرای برنامه تغییر کند) ایجاد کنیم. این کلاس یک کلاس جنریک است که باید هنگام تعریف یک شی از این کلاس، نوع داده‌ مقادیری که قرار است در این آرایه قرار بگیرند را مشخص کنیم.

به کد زیر دقت کنید:

همانطور که می‌بینید در خط اول جلوی نام کلاس بین > و < نام نوع داده مورد نظر خود را نوشتیم. همچنین هنگام ایجاد شی نیز یک <> در جلوی نام کلاس قرار دادیم. شکل کلی استفاده از یک کلاس جنریک به همین صورت است. چون ما نوع داده String را برای کلاس مشخص کردیم پس فقط می‌توانیم مقادیری از جنس String را در این آرایه قرار دهیم که همانطور که می‌بینید با متد add این کار را انجام دادیم.

نوع داده‌های مجاز برای استفاده در جنریک‌ها

نکته‌ای که باید به آن توجه داشته باشید این است که نوع داده‌ای که به یک کلاس یا متد جنریک می‌دهیم حتما باید یک Reference Type باشد (نوع داده ارجاعی) بنابراین نمی‌توان از نوع داده‌های اصلی مثل int استفاده کرد. البته این مشکل خاصی نیست و در جاوا این مسئله پیش بینی شده است. برای اینکه بتوانید از نوع داده‌های اصلی در جنریک‌ها استفاده کنید باید از Wrapper Class نوع داده مربوطه استفاده کنید. یک کلاس Wrapper کلاسی است که یک مقدار از یک نوع داده اصلی را اصطلاحا در خود می‌پیچاند (Wrap) و بنابراین می‌توان مقداری از نوع داده اصلی داشت که با آن مثل یک نوع داده ارجاعی رفتار شود.

در جدول زیر کلاس Wrapper مربوط به هر نوع داده اصلی را مشاهده می‌کنید:

نوع داده اصلی کلاس Wrapper
boolean Boolean
char Character
byte Byte
short Short
int Integer
long Long
float Float
double Double

این کلاس‌ها در پکیج java.lang قرار دارند و بنابراین برای استفاده از آن‌ها نیازی به import کردن نداریم.

به کد زیر دقت کنید:

این کد به دو دلیل غیر عادی به نظر می‌رسد:

در خط اول یک متغیر از نوع کلاس Integer تعریف کردیم و مستقیما به آن یک مقدار عددی نسبت دادیم بدون آنکه بخواهیم با استفاده از new از کلاس Integer شی بسازیم. به این کار boxing گفته می‌شود و برای سهولت کار با این کلاس‌ها پیش‌بینی شده است.

در خط دوم هم متغیر اول را به یک متغیر از نوع داده int نسبت دادیم. قاعدتا این کار نباید درست باشد چون نمی‌توان یک متغیر از یک نوع داده ارجاعی را به یک متغیر از نوع داده اصلی نسبت داد اما برای کلاس‌های Wrapper این قابلیت پیش‌بینی شده تا کار با آن‌ها راحت‌تر شود. به این کار unboxing گفته می‌شود.

تعریف یک کلاس جنریک

به کلاس جنریک ساده زیر دقت کنید:

در این کد منظور از T همان نوع داده‌ایست که هنگام ساخت شی از این کلاس به آن داده می‌شود. البته انتخاب نام T الزامی نیست اما معمولا از این حرف برای نام گذاری پارامترهای جنریک کلاس استفاده می‌شود.

حال به شرح محتویات کلاس می‌پردازیم:

در خط اول یک فیلد از نوع T تعریف شده است.

سپس یک سازنده تعریف کردیم که یک پارامتر به نام item از نوع T می‌گیرد و آن را به فیلد item نسبت می‌دهیم.

این کلاس یک متد به نام getItem دارد که نوع داده برگشتی آن T است و مقدار فیلد item را بر می‌گرداند.

نکته: در این کلاس تمام داده‌ها از نوع T بود (همان نوع داده‌ای که هنگام ساخت شی به این کلاس داده می‌شود) اما محدودیتی در استفاده از نوع داده‌های دیگر (اصلی یا ارجاعی) در این کلاس نداریم.

حال می‌خواهیم از کلاسی که تعریف کردیم استفاده کنیم:

مقدار 13.9f ای که در پرانتز می‌بینید همان مقداری است که سازنده این کلاس می‌گیرد و باید از جنس Float باشد. (چون ما اینجا نوع داده Float را به کلاس Gen دادیم)

در تعریف کلاس‌های جنریک محدود به داشتن فقط یک پارامتر جنریک نیستیم. به عنوان مثال به کلاس زیر دقت کنید:

همانطور که می‌بینید یک پارامتر به نام E به کلاسی که قبلا نوشته بودیم اضافه کردیم و به دنبال آن یک فیلد و یک متد دیگر هم اضافه کردیم که همانطور که مشخص است با E کار می‌کنند.

حال اگر بخواهیم از این کلاس یک شی ایجاد کنیم باید دو نوع داده به آن بدهیم. همچنین از آنجا که یک پارامتر دیگر به سازنده این کلاس وارد شده باید یک مقدار از نوعی که برای E مشخص کردیم نیز به سازنده بدهیم:

محدودسازی پارامترهای جنریک

بار دیگر کلاس ساده‌ مثال‌های قبل را می‌نویسیم:

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

به عنوان مثال فرض کنید در کلاس Gen بخواهیم T را فقط محدود به نوع داده‌های عددی کنیم:

کلاس‌های Wrapper مربوط به نوع داده‌های اصلی عددی همگی از یک کلاس به نام Number ارث بری دارند. حال باید مشخص کنیم که پارامتر T فقط می‌تواند از کلاس‌هایی باشد که از کلاس Number ارث‌بری دارند:

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

مثلا اگر اینترفیسی به نام MyInterface داشته باشیم به این صورت عمل می‌کنیم:

اگر بخواهیم T چند اینترفیس را پیاده‌سازی کرده باشد می‌توانیم نام چند اینترفیس را بعد از extends نوشته و آن‌ها را با , جدا کنیم.

مصطفی نصیری

دانشجوی نرم افزار هستم و علاقه شدیدی به برنامه نویسی مخصوصا با زبان جاوا دارم! در حال حاضر تمرکزم روی اندرویده. دوست دارم چیزایی که یاد میگیرم رو با بقیه به اشتراک بگذارم :)

همچنین ممکن است دوست داشته باشید ...

۲ واکنش

  1. ابوالفضل گفت:

    بسیار عالی 😉

  2. سعید گفت:

    مطالبتو خوندم.کارت درسته

پاسخ دهید

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