Abstract

 

متد abstract

متد abstract متدی است که در همه فرزندانِ یک کلاس وجود دارد ولی در کلاس پدر (یا همان کلاس abstract) امکان پیاده سازی آن وجود نداشته و صرفا پیش الگوی آن در این کلاس وجود دارد.

مثلا عملکردِ راه رفتن در همه حیوان ها وجود دارد، اما کلاس “حیوان” با اینکه “میداند” همه فرزندانش این عملکرد را دارند، اما از نحوه پیاده سازی هریک از آنها اطلاعی ندارد.

کلاس Abstract

کلاسی است که هیچ شیء ای از خودش ساخته نمی شود. باید توجه داشت که اگر کلاسی متدِ abstract داشته باشد، باید خودش هم به عنوان کلاس abstract معرفی شود.

توجه کنید که اگر کلاسی از یک کلاس Abstract دیگر ارث بری کند و تمامی متد های آن را پیاده سازی نکند، خودش هم باید به عنوان کلاس abstract معرفی شود در غیر اینصورت ارور میگیریم.

و باز هم توجه کنید که کلاس می تواند Abstract باشد ولی متدی از نوع Abstract نداشته باشد (با این هدف که اجازه ندهیم شیء ای از خودش ساخته شود) ولی برعکسِ آن امکان پذیر نیست.

Method Binding

به تعیین دقیق متدی که باید برای یک شیء فراخوانی شود متد بایندینگ می گویند. در چند ریختی یا همان polymorphism؛ این کار به صورت انقیاد پویا یا Dynamic binding انجام می شود، یعنی در زمان اجرا مشخص می شود که کدام متد دقیقا باید اجرا شود.

مثلا در قطعه کد:

Animal a;
if(x) a = new Cat();
else a = new Fish();

a.move();

binding برای فراخوانی آخرین خط، به صورت پویا انجام می شود چون کامپایلر تا زمانی که دستور شرطی انجام نشده است، نمیداند a  دقیقا از کدام کلاس است.

به binding در زمان کامپایل، Early binding، static binding یا  compile-time binding گفته می شود.

در حالت کلی، متدهای static، private یا final در زمان کامپایل bind می شوند.

به dynamic binding، اسامی دیگری همچون late binding یا run-time binding نیز اطلاق می شود.

عدم اجرای polymorphism در پارامتر متدها

پارامتر های ورودی متد ها، polymorphism یا چند ریختی را رعایت نمی کنند. مثلا در قطعه کد زیر:

	public void Test(Parent P) {
		//do Something for parent Object
	}
	public void Test(Child P) {
		//Child Extends From Parent.
		//now here do something for a child...
	}
	
	public static void main(String[] args) {
		Parent P = new Child();
		test(P);// this Calls Method with parent Object Input
	}

دستورِ آخر، تابع با داده ورودی عام تر را فراخوانی می کند. در واقع تابع با ورودی Child عملا هرگز و تحت هیچ شرایطی فراخوانی نمی شود. (چون هر کلاسی از child و حتی فرزندان آن نیز، کلاسی از نوعِ Parent محسوب می شوند).

چک کردنِ نوع ارجاع

می توانیم نوعِ یک شیء را در زمان اجرا با دستور:

Ref a = new Ref();
if (a instance of Type){
//...
}

شناسایی کنیم. این دستور یک عبارت بولین بر می گرداند و می تواند داخل یک دستور شرطی قرار بگیرد.

اگر  شیء a و کلاس Type هیچ ارتباطی با یکدیگر نداشته باشند، یعنی اگر Ref، زیرکلاس، اَبرکلاس (super class) یا خودِ Type نباشد؛ این عبارت همواره غلط بر می گرداند.بنابراین کامپایلر همان ابتدا ارور می دهد و خطای کامپایل می گیرد.

اما در غیر این صورت، این دستور در زمان اجرا بررسی می شود.

اگر Type؛ همان Ref یا اَبَرکلاس ( Super Class) ی از کلاس Ref باشد؛ این عبارت همواره True  برمیگرداند، مگر اینکه a، تهی یا null باشد.

دستور getClass

این تابع در کلاس Object پیاده سازی شده است و final می باشد. (میدانیم هر کلاسی به صورت ضمنی، فرزندِ Object هست)؛ این تابع، ClassObject یا شیء کلاسِ یک مربوط به یک شیء را برمیگرداند، کلاس آبجکت، اطلاعاتی مربوط به کلاس مربوط به شیء مورد نظر را در خود دارد.

Animal a = new Dog();
String s = a.getClass().getSimpleName();
s = a.getClass().getName();

در قطعه کد بالا بخشی از کاربرد کلاس آبجکت را مشاهده می کنید، خط دوم اسمِ خودِ کلاسِ مربوط به شیء و دستورِ سوم اسم کامل مربوط به کلاس مربوط به شیء (یعنی به همراه پیشوند پکیج) را به رشته مقداردهی می کند.

در نسخه های قبل از 8 جاوا، کلاس آبجکت ها در محلی به نام PermGen ذخیره می شدند که در بعضی پروژه ها که کلاسهای زیادی در آنها وجود داشت با مشکل پر شدنِ حافظه مواجه می شدیم، با این حال از نسخه 8 به بعد، کلاس آبجکت ها در جایی به اسم MetaSpace ذخیره می شوند که دیگر مشکلات قبلی در آن به چشم نمی خورد.

 

نکاتی درمورد interface ها

هر متدی در داخل یک واسط یا اینترفیس، به صورت ضمنی، public و abstract هست (یعنی لزومی به نوشتنِ اینها در تعریف توابع نیست).

هر متغیر ی هم، به صورت ضمنی public، static و final هست.

یک اینترفیس هم می تواند از یک، یا چند اینترفیس دیگر ارث بری کند، ولی باید ازکلمه extends استفاده کند و نه implements.

به قطعه کد زیر نگاه کنید و نظرتان را در مورد آن بگویید:

public interface A{
void f();
}
public interface B{
int f();
}

abstract class C implements A,B{
//
//
}

اینترفیسِ اول متدی دارد که نام آن f است و خروجی آن void است، و اینترفیس دوم متدی دارد که نامش f و خروجی آن int است، یعنی کلاس C به صورت ضمنی هر دوی این توابع را دارا می باشد. و مسلم است که نمی توان دو تابع با یک نام و با خروجی های مختلف در یک کلاس داشت، بنابراین با سینتکس ارور مواجه می شویم:

The return types are incompatible for the inherited methods Interface1.f(), Interface2.f()

باید توجه داشت که در جاوا، ما ارث بری چندگانه از رفتار نداریم، اما ارث بری چندگانه از نوع داریم.

البته از جاوا 8، ارث بری چندگانه از رفتار نیز به آن اضافه شده است. این کار با متد های پیش فرض در واسط ها انجام می شود.

 

متد های پیش فرض (default methods) در اینترفیس

از جاوا 8؛ واسط ها می توانند متد های غیر Abstract نیز داشته باشند (یعنی توابعی با بدنه پیاده سازی شده)، قطعه کد زیر نحوه تعریف این نوع توابع را نشان می دهد:

	interface Person{
		Date getBirthDate();
		default Integer age() {
			System.out.println("Hooreyy! I am the first method IMPLEMENTED IN "
					+ "an iterface EVER! :) ");
		}
	}

کلاس های داخلی بی نام (Anonymous Inner Classes)

گاهی می توان بدونِ اینکه قابل رویت باشد، به صورت بی نام یک کلاس داخلی تعریف کرد، این عملکرد در مواقعی که میخواهیم کلاس کوچکی تعریف کنیم که فقط یک بار یا فقط برای یک بخش محدود از برنامه یا برای یک شیء اجرا شود بسیار کاربردی است (مثالش تعریف onClickListener برای دکمه ها در اندروید است که اگر با آن آشنایی دارید کاملا متوجه عملکردِ آن می شوید).

قطعه کد زیر نحوه انجام اینکار را نشان می دهد:

public abstract class Class1{

	interface Protocol {
		void behaviour();
	}
	
	public static void main(String[] args) {
		
		Protocol P = new Protocol() {
			
			@Override
			public void behaviour() {
				// TODO Auto-generated method stub
				
			}
		};
	}
}

در واقع به صورتِ ضمنی، ما در قطعه کدِ بالا، کلاسی ساخته ایم که این interface را implement کرده است و توابع آن را پیاده سازی کرده است (کلاسی که هیچ اسمی ندارد! کلاسِ یکبار مصرف).

کلاس داخلی بی نام معمولا برای interface ها استفاده می شود، ولی برای کلاس های abstract و حتی کلاس های معمولی هم قابل انجام است. امتحان کنید!