Generic Type Erasure
توضیحات جلسه
جزوه و مستندات
مفاهیم کلیدی
- Type Erasure
فرآیندی است که در آن کامپایلر تمام اطلاعات مربوط به Generic را در زمان Compile Time حذف میکند تا کد با نسخههای قدیمیتر جاوا سازگار باشد. در واقع، خروجی نهایی یا همان Bytecode فاقد اطلاعات جنریک است.
- Raw Type
به حالتی گفته میشود که یک کلاس یا اینترفیس جنریک بدون مشخص کردن Type Parameter استفاده شود؛ برای مثال، استفاده از List بهجای List<String>. کامپایلر در فرآیند پاکسازی نوع، پارامترها را با این حالت جایگزین میکند.
- Upper Bound
محدودیتی است که با کلمه کلیدی extends روی Type Parameter اعمال میشود؛ مانند <T extends Number>. این کار باعث میشود کامپایلر بداند که تایپ ورودی حتماً باید از یک کلاس خاص مشتق شده باشد.
- Diamond Operator
علامت <> است که در جاوا برای جلوگیری از تکرار تایپ هنگام نمونهسازی استفاده میشود. کامپایلر در زمان Compile Time محتوای داخل این عملگر را حذف میکند.
- Casting
عملیاتی است که کامپایلر پس از حذف اطلاعات جنریک انجام میدهد تا مطمئن شود مقادیر بازگشتی با نوع مورد انتظار برنامه هماهنگ هستند و امنیت کد یا Type Safety حفظ میشود.
موارد مصاحبه ای
- مفهوم Type Erasure چیست؟
پاکسازی نوع فرآیندی است که طی آن کامپایلر جاوا اطلاعات جنریک را حذف میکند و آنها را با تایپهای واقعی، مانند Object یا کرانهای بالا، جایگزین میکند تا در زمان Runtime مشکلی برای اجرای کد وجود نداشته باشد.
- اگر یک تایپ پارامتر بدون محدودیت Unbounded باشد، با چه چیزی جایگزین میشود؟
اگر پارامتر بهصورت <T> تعریف شده باشد، کامپایلر آن را با کلاس Object جایگزین میکند.
- تایپ پارامتر در حالت Bounded، مانند
<T extends Number>، به چه چیزی تبدیل میشود؟
در این حالت، کامپایلر تمام استفادههای T را با اولین کران بالا جایگزین میکند. در مثال <T extends Number>، تایپ T با Number جایگزین میشود.
- در صورت وجود چندین محدودیت Multiple Bounding، مانند
<T extends A & B>، چه اتفاقی میافتد؟
کامپایلر Type Parameter را با اولین کلاس یا اینترفیس ذکرشده در لیست محدودیتها جایگزین میکند. در این مثال، T با A جایگزین میشود.
- چرا در خروجی نهایی بایتکد، اثری از جنریک نیست؟
چون جنریکها در جاوا در سطح کامپایلر مدیریت میشوند. برای حفظ سازگاری عقبرو یا Backward Compatibility با نسخههای قدیمی JVM، اطلاعات جنریک در لایه Bytecode حذف میشوند.
سناریو کاربردی
فرض کنید یک کلاس جنریک به نام Stack<E> دارید که درون آن آرایهای از جنس E تعریف شده است.
۱. قبل از کامپایل
شما کدی مینویسید که در آن Stack<String> ایجاد شده و فقط مقدارهای رشتهای دریافت میکند. این کار امنیت کد شما را در مرحله توسعه تضمین میکند.
۲. در مرحله Type Erasure
کامپایلر تمام Eها را بررسی میکند. اگر E محدود نشده باشد، آن را به Object تبدیل میکند. بنابراین کلاس شما در Bytecode بهصورت یک Stack معمولی دیده میشود که با Object کار میکند.
۳. نتیجه نهایی
اگر متدی در این کلاس مقدار بازگشتی از جنس E داشته باشد، کامپایلر بهصورت خودکار یک Casting به String در محل فراخوانی متد اضافه میکند تا برنامه بدون خطا اجرا شود.
بیشتر بدانید
- نکته کامپایلر
پاکسازی نوع فقط در سطح کلاسها اتفاق نمیافتد، بلکه در سطح Methodها نیز انجام میشود. اگر متدی ورودی جنریک بگیرد، امضای آن در Bytecode تغییر میکند و به تایپ پایه، مانند Object، تبدیل میشود.
- قانون جایگزینی
همیشه به یاد داشته باشید که در فرآیند پاکسازی، اولویت جایگزینی با اولین Bound ذکرشده است. این موضوع در طراحی معماری کلاسهای جنریک اهمیت زیادی دارد.
- امنیت در بایتکد
با وجود حذف اطلاعات جنریک، امنیت کد از بین نمیرود؛ زیرا کامپایلر قبلاً در مرحله Compile Time تمام بررسیهای لازم را انجام داده و کدهای Casting لازم، یعنی Checkcast، را در Bytecode تزریق کرده است.
