Generic Limitation
توضیحات جلسه
جزوه و مستندات
مفاهیم کلیدی
- Type Erasure
فرآیندی در کامپایلر جاوا است که طی آن، تمام اطلاعات مربوط به Genericها حذف میشود و با نوع Object یا Bound مربوطه جایگزین میشود تا کد با نسخههای قدیمیتر جاوا سازگار باشد.
- Reflection
قابلیتی در جاوا است که اجازه میدهد در زمان اجرا یا Run-time به ساختار کلاسها، متدها و فیلدها دسترسی داشته باشیم و برخی محدودیتهای معمول Genericها را مدیریت کنیم.
- Primitive Type
انواع دادهای پایه مانند int و boolean هستند. چون این نوعها از Object ارثبری نمیکنند، نمیتوانند بهعنوان آرگومان در Genericها استفاده شوند.
- Wrapper Class
کلاسهای معادل برای انواع دادهای پایه هستند؛ مانند Integer برای int. این کلاسها اجازه میدهند از نوعهای Primitive در محیطهای شیءگرا و Generic استفاده کنیم.
- Static Member
اعضایی از کلاس، مانند متد یا فیلد، هستند که متعلق به خود کلاساند و نه به یک Instance خاص. به همین دلیل نمیتوانند از Generic Type کلاس استفاده کنند.
موارد مصاحبه ای
- چرا نمیتوان یک نمونه از Generic Type را مستقیماً با دستور
new T()ایجاد کرد؟
به دلیل عملیات Type Erasure، در زمان اجرا یا Run-time نوع واقعی T حذف میشود و به Object تبدیل میشود. بنابراین JVM نمیداند دقیقاً از چه کلاسی باید نمونهسازی کند.
- دلیل محدودیت استفاده از Primitive Typeها در Genericها چیست؟
در جاوا، Genericها بر پایه Object کار میکنند و Type Erasure آنها را به Object تبدیل میکند. چون نوعهای Primitive از Object ارثبری نمیکنند، نمیتوانند جایگزین Generic Typeها شوند.
- چرا تعریف فیلدهای استاتیک از نوع Generic، مانند
static T myField، مجاز نیست؟
فیلدهای استاتیک بین تمام نمونههای یک کلاس مشترک هستند، در حالی که Generic Type در هر Instance میتواند متفاوت باشد؛ برای مثال، یک شیء با Integer و شیء دیگر با String ساخته شود. این تضاد مانع از تعریف فیلد استاتیک از نوع Generic میشود.
- تفاوت آرایهها و Genericها در برخورد با نوع داده Type چیست؟
آرایهها نوع داده خود را در زمان اجرا حفظ میکنند؛ یعنی Reified هستند. اما Genericها در زمان اجرا نوع خود را از دست میدهند. به همین دلیل نمیتوان مستقیماً یک آرایه جنریک مانند new T ایجاد کرد.
- چگونه میتوان محدودیت
instanceofرا برای Genericها برطرف کرد؟
چون در زمان اجرا T وجود ندارد، نمیتوان از instanceof T استفاده کرد. راهحل جایگزین، استفاده از Reflection و متد isInstance() روی کلاس مربوط به آن نوع داده است.
سناریو کاربردی
در این ویدیو، برای مدیریت محدودیتهای Generic، از یک سناریوی مبتنی بر Reflection استفاده شده است.
برای اینکه بتوانیم در زمان اجرا متوجه شویم T چه نوع دادهای است یا از آن نمونهسازی کنیم، در سازنده یا Constructor کلاس، یک شیء از نوع Class<T> دریافت میکنیم.
با ذخیره کردن این Class در یک فیلد، میتوانیم از متد newInstance() برای ساختن اشیای جدید یا از متد isInstance() برای بررسی نوع داده در زمان اجرا استفاده کنیم.
این روش به برنامه اجازه میدهد با وجود حذف شدن Genericها توسط کامپایلر، همچنان به نوع واقعی داده دسترسی داشته باشد.
بیشتر بدانید
- نکته برنامه نویسی
برای استفاده از اعداد یا مقادیر منطقی در Genericها، حتماً باید از Wrapper Classها استفاده کنید. برای مثال، بهجای int از Integer و بهجای char از Character استفاده کنید.
- نکته IDE
در محیط IntelliJ IDEA، اگر هنگام کار با Reflection و تبدیل نوع یا Casting با هشدارهای زردرنگ مواجه شدید، میتوانید در صورت اطمینان از صحت کد، از انوتیشن @SuppressWarnings("unchecked") استفاده کنید.
البته توصیه میشود تا حد امکان کد را طوری بنویسید که کامپایلر بتواند صحت آن را تضمین کند.
- راه حل آرایههای Generic
برای ساخت آرایه از نوع Generic، بهجای دستور مستقیم new باید از متد استاتیک Array.newInstance در پکیج java.lang.reflect استفاده کرد. این متد، نوع کلاس و طول آرایه را بهعنوان ورودی دریافت میکند.
