گاهی مواقع منطق کد ما در شرایط مختلف یکسان است و فقط جنس آن فرق میکند و اگر برای شرایط مختلف کد هایی که عملکرد یکسانی دارند و فقط از نظر دیتا تایپ آن فرق دارد پیاده سازی های مشابه ای را داشته باشیم اینجا دچار Code Redundancy شده ایم در این شرایط نگه داری کد و توسعه کد بسیار سخت تر خواهد شد و اصول طراحی قوی ای نخواهد داشت
داده های عمومی یا Generic ها در جاوا برای مشخص کردن نوع خاص از Object که قرار است با آن درون کلاس یا متد کار کنیم استفاده میکنیم ، منظق پیاده سازی یکسان است ولی نوع داده را محدود میکنیم به آنچیزی که میخواهیم در Runtime با آن کار کنیم و این انعطاف پذیری بالایی در شی گرایی به برنامه نویس میدهد
Type Parameter : به آن نوع دیتا تایپی که در Generic استفاده میکنیم گفته میشود که نمیتوان از نوع primitive data types استفاده کرد و حتما باید از نوع کلاس باشد
List<String> list ;
وقتی ما Type Parameter خاصی را در Generic تعریف میکنیم موقع کار با آن چون محدود به متد و فیلد های ان نوع از کلاس هستیم جلوی اشتباهات آتی کد نویسی را هم میگیرد و در صورت وقوع خطای Syntax Error میدهد
ایجاد محدودیت از طریق درخت وراثت : ما در Generic میتوانیم Type Parameter را محدود به پدر خاصی کنیم تا یک خانواده از کلیه فرزندان آن پدر را در بر بگیرد و اینطوری قابلیت بیشتری را به کدمان بدهیم
class MyNumber <E extends Number > {...}
Raw Type : وقتی ما روی Generic ها Type Parameter را مشخص نکرده باشیم میتوان هر نوع کلاسی را استفاده کنیم مگر توسط extends محدود شده باشد ولی دیگر نوع خاص تر آن را مشخص نکرده ایم
Type Interface : از جاوا 7 به بعد اضافه شده و مفهمومش به این طورت است که وقتی در متغییر Reference نوع Type Parameter را مشخص کردیم دیگر نیازی نیست در Constructor هم انرا مشخص کنیم با این عمل Diamond Operator میگویند
یک کلاس Generic میتواند از هر نوع کلاس دیگری ارث بری یا ارث دهی کند
generic method : یک متد به تنهایی میتواند از خاصیت Generic استفاده کند برای تعریف آن باید قبل از تعیین مقدار برگشتی متد آنرا تعریف کنیم :
public class GenericClass <E> {
public <T> void genericMethod1 (int i , T t , E e ){...}
public <A> A genericMethod2 (A a){...}
}
همانطور که در کد بالا میبینیم هم کلاس نوع Generic است و هم متد به تنهایی نوع Generic و در آرگومان متد از هر دو Type Parameter استفاده میکند در متد دوم مقدار برگشتی از نوع Type Parameter است
Erasure یا فرآیند محو : کنترلی که روی Generic ها انجام میشود فقط در زمان Compile Time است وقتی کامپایل کد تمام میشود و برنامه اجرا میگردد اثری از Type Parameter در زمان Runtime وجود ندارد
اگر یک شی از نوع Type Parameter ایجاد شود فقط توسط کامپایلر این موضوع چک میشود که دیتا تایپ مورد استفاده از همان نوع باشد
در واقع همه Generic Type ها به صورت Raw Type تفسیر میشود و به این رفتار جاوا در بحث Generic ها Erasure گفته میشود
محدودیت Generic :
روی Type Parameter نمیتوان عملیاتی انجام داد چون نوعش مشخص نیست
نمیتوان از آن new کرد
نمیتوان نوعش را با instanceof مشخص کرد
کلاس Generic نمیتواند از Exception ها ارث بری کند ولی خود Type Parameter ها میتوانند از Exception ارث بری داشته باشند
Wildcard ها در Generic : فرض کنید یک Generic Class دارید که نوع های مختلفی را می پذیرد و از آن چند نمونه داریم
class Box<T> {
public T getValue(){...}
}
Box<Student> student = new Box<>();
Box<Teacher> teacher = new Box<>();
و در متدی دیگر میخواهیم با استفاده از getValue مقدار آنرا بخوانیم و بعد toString آنرا چاپ کنیم ولی کلاس Box را با Type Parameter های مختلفی ساخته ایم و برای هر نوع باید یک متد برای print کردن بنویسیم راه حل استفاده از wildcard است :
public void doPrint(Box<?> box){
System.out.println ( box.getValue() .toString() );
}
علامت ؟ به unknown (ناشناخته) معروف است و میتواند شامل هر نوع دیگری از Type Parameter ها شود و در حین اجرا نوع آن مشخص میشود
unknown type parameter ها میتوانند توسط ارث بری و extends به نوع خاص تری محدود شوند