* Абстрактни класове и интерфейси
Публикувано на 09 октомври 2009 в раздел ПИК3 Java.
Абстрактни са тези методи в клас, чиято реализация не е имплементирана. С други думи ние сме декларирали, че такъв метод трябва да има, но той все още не е описан как ще работи. Класове, които съдържат в себе си абстрактни методи наричаме "абстрактни класове".
В статията за класове и обекти дефинирахме "класовете" като "понятия". Когато говорим за "абстрактни класове", то би трябвало да разбираме "абстрактни понятия". По-доброто определение за такива класове обаче е "шаблони". В действителност когато декларираме методи, чиято реализация още не е изпълнена, ние даваме нещо като шаблон на наследниците на тези класове, който те просто трябва да "попълнят". Не е възможно да се правят инстанции на абстрактни класове (противоречи и с обикновената логика, защото какво би трябвало да означава "абстрактен реален обект"?).
Нека дадем един класически пример за абстрактен клас. Ще създадем клас "геометрична фигура", в която ще има абстрактен метод, наречен "лице":
abstract class Figure{ double dim1; double dim2; public Figure(double dim1, double dim2){ this.dim1 = dim1; this.dim2 = dim2; } abstract double getArea(); }
Ние не можем да кажем колко ще бъде лицето на фигурата и колко ще бъде нейния периметър, защото все още не знаем точно каква ще е тя - правоъгълник или успоредник при който dim1 и dim2 са страните, триъгълник при който dim1 е основа, а dim2 е височина към нея и т.н. Когато наследим клас Figure обаче можем да дадем имплементация на този метод:
class Rectangle extends Figure{ public Rectangle(double a, double b) { super(a,b); } public double getArea(){ return this.dim1 * this.dim2; } } class Triangle extends Figure{ public Triangle(double a, double b) { super(a,b); } public double getArea(){ return (this.dim1 * this.dim2)/2; } }
Още по-голямо ниво на абстракция е понятието "интерфейс". Интерфейсът съдържа абстрактни методи и евентуално константи (за константи ще говорим по-късно). В по-късни версии на Java се появяват и "методи по подразбиране", както и private методи, но на този етап няма да ги разглеждаме. Чрез интерфейсите ние даваме само и единствено обща насоченост как трябва да изглежда даден клас, който го "имплементира". Нека преработим горния пример да работи по същия начин, но чрез интерфейс:
interface Figure{ public double getArea(); } class Rectangle implements Figure{ double dim1; double dim2; public Rectangle(double a, double b) { this.dim1 = a; this.dim2 = b; } public double getArea(){ return this.dim1 * this.dim2; } } class Triangle implements Figure{ double dim1; double dim2; public Triangle(double a, double b) { this.dim1 = a; this.dim2 = b; } public double getArea(){ return (this.dim1 * this.dim2)/2; } }
Границата на това кога да използваме абстрактен клас и кога интерфейс зависи от логиката на приложението. Ако решим да включим нова геометрична фигура "окръжност", то може би интерфейсът ще бъде по-подходящ - за лицето на окръжност не ни трябват две размерности, а ни трябва само един радиус (тоест наследяването на показания абстрактен клас "Figure" би било нелогично, защото той съдържа две полета). Когато обаче искаме да създадем шаблон на фигура, която е задължително с две размерности, то шаблона на абстрактния клас би бил по-подходящ. С други думи отговорът на въпроса "интерфейс или абстрактен клас да използвам" е "зависи доколко абстрактно е понятието, което ще реализираме". По принцип ще спазваме правилото "конкретизираме и даваме колкото се може повече насоки". Понякога ще е удачно да спазваме пълната йерархия - интерфейс, после абстрактен клас, който го имплементира, след това клас, който наследява абстрактния. Ще си позволим да въведем следните по-общи дефиниции, които също могат да служат за отправна точка:
Интерфейс: съвкупност от декларации на действия, които даден тип обекти трябва да може да извършва.
Абстрактен клас: изнесена като родителски клас базова функционалност за даден тип обекти.
class Rectangle implements Figure{
double dim1;
double dim2;
public Rectangle(double a, double b) {
this.dim1 = dim1;
this.dim2 = dim2;
}
public double getArea(){
return this.dim1 * this.dim2;
}
}
Тук защо е this.dim1 = dim1;
this.dim2 = dim2;
вместо this.dim1 = a;
this.dim2 = b;
?
Грешката е очевидна - ще поправя кода.