متن خبر

نحوه استفاده از Generics در جاوا – با مثال کد توضیح داده شده است

نحوه استفاده از Generics در جاوا – با مثال کد توضیح داده شده است

شناسهٔ خبر: 648459 -




در برنامه جاوا خود، ممکن است هنگام کار با انواع مختلفی از اشیاء مانند Integer، String و غیره ClassCastException مخوف در زمان اجرا مواجه شده باشید. این خطا بیشتر به دلیل ارسال یک شی به نوع داده اشتباه است.

در این مقاله، با ژنریک ها آشنا می شوید و خواهید دید که چگونه می توانند به رفع این مشکل کمک کنند.

چرا به ژنریک نیاز داریم؟

بیایید با یک مثال ساده شروع کنیم. ابتدا انواع مختلفی از اشیاء را به یک ArrayList اضافه می کنیم. در مرحله بعد، سعی می کنیم آنها را بازیابی کنیم و مقادیر آنها را چاپ کنیم.

 List list = new ArrayList(); list.add("Hello"); String str = (String) list.get(0); System.out.println("String: " + str);
مثال بدون استفاده از Generics - ریخته گری صریح

همانطور که می بینید، ما یک شی String را به ArrayList اضافه کرده ایم. از آنجایی که ما کسانی هستیم که کد را نوشته ایم، می دانیم که عنصر چه نوع شی را نشان می دهد، اما کامپایلر آن را نمی داند. پس ، هنگامی که سعی می کنیم مقدار را از فهرست بازیابی کنیم، یک Object برمی گردانیم و باید یک Casting صریح انجام دهیم.

 list.add(123); String number = (String) list.get(1); System.out.println("Number: " + number);
مثال بدون استفاده از Generics - ClassCastException را پرتاب می کند

اگر یک Integer را به همان فهرست اضافه کنیم و سعی کنیم مقدار را واکشی کنیم، یک ClassCastException دریافت می کنیم زیرا یک شی عدد صحیح را نمی توان به یک رشته فرستاد.

با استفاده از Generics، می‌توانیم هر دو مشکلی را که در بالا توضیح داده شد، حل کنیم. بیایید ببینیم چگونه.

ابتدا باید از عملگر الماس استفاده کنیم و نوع شیء موجود در این فهرست را محدود کنیم. ما باید نوع شی را به صراحت در عملگر الماس ذکر کنیم. این یک تحلیل زمان کامپایل را اعمال می کند، پس دیگر مجبور نیستید ارسال صریح را انجام دهید. همچنین می توانید ClassCastException حذف کنید.

 List<String> list = new ArrayList(); list.add("Hello"); String str = list.get(0); // No need for explicit casting System.out.println("String: " + str); list.add(123); // Throws compile-time error
مثال با Generics

قراردادهای نامگذاری برای پارامترهای نوع

در مثال قبلی، مشاهده کردید که استفاده از List<String> نوع شیئی را که فهرست می تواند در خود جای دهد محدود می کند. مثال زیر از یک کلاس Box و نحوه عملکرد آن با انواع مختلف داده را تحلیل کنید.

 public class Box<T> { private T value; public void setValue(T value) { this.value = value; } public T getValue() { return value; } public static void main(String[] args) { Box<String> stringBox = new Box<>(); stringBox.setValue("Hello, world!"); System.out.println(stringBox.getValue()); Box<Integer> integerBox = new Box<>(); integerBox.setValue(123); System.out.println(integerBox.getValue()); } }

به نحوه اعلان کلاس Box<T> توجه کنید. در اینجا، T یک پارامتر نوع است، که نشان می دهد کلاس Box می تواند با هر شی از آن نوع کار کند. همین امر در روش اصلی نشان داده شده است که در آن نمونه ای از Box<String> و Box<Integer> هر دو مجاز به ایجاد هستند، پس ایمنی نوع را تضمین می کند.

طبق اسناد رسمی :

طبق قرارداد، نام پارامترهای نوع حروف تک و بزرگ هستند.

متداول ترین نام های پارامتر نوع عبارتند از:

E - عنصر (به طور گسترده توسط Java Collections Framework استفاده می شود)
ک - کلید
ن - شماره
T - نوع
V - ارزش
S، U، V و غیره - انواع 2، 3، 4

بیایید نگاهی به نحوه نوشتن یک روش عمومی بیندازیم. در زیر این کنوانسیون آمده است:

 public static <T> void printArray(T[] inputArr) { for (T element : inputArr) { System.out.print(element + " "); } System.out.println(); }

در اینجا، یک آرایه از هر نوع را می گیریم و عناصر آن را چاپ می کنیم. توجه داشته باشید که قبل از نوع برگشتی متد، باید پارامتر نوع عمومی T در براکت های زاویه <> مشخص کنید. بدنه متد روی آرایه ای که به عنوان پارامتر ارسال کرده ایم، از هر نوع T ، تکرار می شود و هر عنصر را چاپ می کند.

 public static void main(String[] args) { // Create different arrays of type Integer, Double and Character Integer[] intArr = {1, 2, 3, 4, 5}; Double[] doubleArr = {1.1, 2.2, 3.3, 4.4, 5.5}; Character[] charArr = {'H', 'E', 'L', 'L', 'O'}; System.out.println("Integer array contains:"); printArray(intArr); // pass an Integer array System.out.println("Double array contains:"); printArray(doubleArr); // pass a Double array System.out.println("Character array contains:"); printArray(charArr); // pass a Character array }

ما می توانیم این روش عمومی را با عبور از انواع مختلف آرایه ها ( Integer ، Double ، Character ) فراخوانی کنیم و خواهید دید که برنامه شما عناصر هر یک از این آرایه ها را چاپ می کند.

محدودیت های ژنریک

در Generics، ما از کران‌ها برای محدود کردن انواعی که یک کلاس، رابط یا متد عمومی می‌تواند بپذیرد، استفاده می‌کنیم. دو نوع وجود دارد:

1. مرزهای بالایی

این برای محدود کردن نوع عمومی به حد بالایی استفاده می شود. برای تعریف کران بالا، از کلمه کلیدی extends استفاده می کنید. با تعیین یک کران بالا، مطمئن می شوید که کلاس، رابط یا متد، نوع مشخص شده و همه زیر کلاس های آن را می پذیرد.

نحو به صورت زیر خواهد بود: <T extends SuperClass> .

اگر همان کلاس Box را در نظر بگیرید که قبلاً استفاده می کردیم، می توان آن را به صورت زیر تغییر داد:

 class Box<T extends Number> { private T value; public void setValue(T value) { this.value = value; } public T getValue() { return value; } }

در این مثال، T می تواند هر نوعی باشد که Number گسترش دهد، مانند Integer ، Double یا Float .

2. مرزهای پایین تر

این برای محدود کردن نوع عمومی به حد پایین تر استفاده می شود. برای تعریف کران پایین، از کلمه کلیدی super استفاده می کنید. با تعیین یک کران پایین، مطمئن می شوید که کلاس، رابط یا متد، نوع مشخص شده و همه سوپرکلاس های آن را می پذیرد.

نحو به صورت زیر خواهد بود: <T super SubClass> .

برای نشان دادن استفاده از کران پایین، اجازه دهید به مثال زیر نگاهی بیندازیم:

 public static void printList(List<? super Integer> list) { for (Object element : list) { System.out.print(element + " "); } System.out.println(); }

استفاده از کران پایین <? super Integer> تضمین می‌کند که می‌توانید نوع مشخص شده و همه سوپرکلاس‌های آن را که در این مورد فهرستی از Integer ، Number یا Object خواهد بود را به متد printList منتقل کنید.

Wildcards چیست؟

? که در مثال قبل دیدید Wildcard نامیده می شود. می توانید از آنها برای اشاره به یک نوع ناشناخته استفاده کنید.

می توانید از یک علامت عام با کران بالایی استفاده کنید، در این صورت چیزی شبیه به این خواهد بود: <? extends Number> . همچنین می توان آن را با کران پایین تر مانند <? super Integer> .

پاک کردن را تایپ کنید

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

کامپایلر از اطلاعات نوع عمومی که در دسترس است برای اطمینان از ایمنی نوع استفاده می کند. در فرآیند پاک کردن نوع:

اگر نوع نامحدود باشد، پارامترها با کران یا نوع Object خود جایگزین می شوند

اگر نوع محدود باشد، پارامترها با کران اول جایگزین می شوند و اطلاعات نوع عمومی پس از کامپایل حذف می شوند.

اگر به مثال کلاس Box نگاهی بیندازیم:

 public class Box<T> { private T value; //getters and setters }
قبل از پاک کردن نوع (نامحدود)

کد بالا به این شکل می شود:

 public class Box { private Object value; //getters and setters }
پس از پاک کردن نوع (نامحدود)

نتیجه

در این مقاله، مفهوم ژنریک در جاوا و نحوه استفاده از آنها را با چند مثال اساسی تحلیل کردیم. درک و استفاده از ژنریک ایمنی نوع در برنامه شما را افزایش می دهد. آنها همچنین نیاز به ارسال صریح را از بین می برند و کد شما را قابل استفاده مجدد و قابل نگهداری می کنند.

بیایید در لینکدین وصل شویم.

خبرکاو

ارسال نظر




تبليغات ايهنا تبليغات ايهنا

تمامی حقوق مادی و معنوی این سایت متعلق به خبرکاو است و استفاده از مطالب با ذکر منبع بلامانع است