متدهای پیشفرض در جاوا 8
متدهای پیشفرض در واسطها، برای اولین بار در جاوا 8 معرفی شد. در این مقاله توضیح میدهیم که متدهای پیشفرض چه هستند و چه تغییری در طراحی API جاوا ایجاد کردهاند.
یک طراحی عادی
قبلا، واسطها در جاوا تنها میتوانستند متدهای بدون بدنه و بدون پیادهسازی داشته باشند و در واقع فقط امضای (signature) متدها در واسط نوشته میشد.
برای اینکه بتوان به این طراحی، پیادهسازی اضافه کرد، حتما نیاز به یک کلاس (abstract یا غیر abstract) بود. به این ترتیب، طراحی API قدیمی، دارای چنین سلسهمراتبی بود:
- در ریشه، واسط قرارداد و امضای متد را تعریف میکند.
- کلاس میانی، رفتار مشترک را پیادهسازی میکند.
- اگر نیازی بود، یک کلاس در این سلسهمراتب، این رفتار را بازنویسی (override) میکند.
مساله
تا زمانی که کلاسهای خارج از دسترسِ طراح API بتواند واسط را پیادهسازی کند، این طراحی بینقص است. سلسهمراتب زیر، بخش List از Collections API جاوا به همراه کلاس سفارشیِ MyList را نشان میدهد.
بیایید متد ()sort را در واسط List تعریف کنید. تنها کلاسهای AbstractList و MyList واقعا باید این متد را پیادهسازی کنند.
واضح است که احتمال دارد یک پیادهسازی کاملا یکسان برای متد ()sort را در هر دو کلاس قرار دهیم (کاملا منطقی هم هست). به این ترتیب، پیادهسازیهای مستقیمِ واسط List، مجبورند متد ()sort از AbstractList را تکرار (duplicate) کنند.
به منظور جلوگیری از تکرار کد، طراحان API جاوا مجبور شدند متد ()sort را از واسط List به یک کلاس نامرتبط (Collections) که تنها دارای متدهای static است، منتقل کنند.
با این کار، مشکل کدهای مشترک حل میشود و حالا دیگر تنها یک متد واحد مسئولیت مرتبسازی را برعهده دارد.
در مقابل، متدهای static، شیگرا نیستند و بدتر از آن، هیچ رابطهای از List به Collections در کد وجود ندارد (البته یک رابطه در جهت عکس وجود دارد). به این ترتیب، اگر کسی از وجود کلاس Collections و ویژگیهایش مطلع نباشد، هیچ راهی وجود ندارد که مطلع شود.
راه حل: متدهای پیشفرض
حالا تصور کنید که پیادهسازی کد در واسط امکانپذیر بود. در این صورت، متد ()sort میتوانست در واسط List پیادهسازی شود و نمودار کلاسهایمان شبیه شکل زیر میشد:
به این ترتیب، مشکل بالا حل میشد. به صورت پیشفرض، هر پیادهسازی لیست، از طریق وراثت به متد ()sort دسترسی خواهد داشت.
دقیقا همین موضوع، دلیل وجود متدهای پیشفرض است. نه کمتر و نه بیشتر.
جهت رفع کنجکاوی: پیادهسازی ()Collections.sort به صورت زیر بازنویسی شده است تا وظیفه مرتبسازی به متد پیشفرض محول شود:
// Without generics for better readability public class Collections { public static void sort(List list) { list.sort(null); } }
جمعبندی
اگر کارتان به جایی رسید که مجبور شدید به جای اینکه یک تکهکد را در یک واسط مشترک قرار دهید، آن را در کلاسهای مختلف تکرار کنید، در این صورت، استفاده از متدهای پیشفرض به جای استفاده از کلاسهای کمکی، با اختلاف، راه حل هوشمندانهتری خواهد بود.
مقاله مفید و جالبی بود. برام سوال پیش اومده بود که با وجود امکان متد پیشفرض در واسطها، چه نیازی به کلاسهای کمکی داریم.