1. Представяне на интерфейси
Днес е вашият ден за знания. Друга нова и интересна тема са интерфейсите.
Концепцията за интерфейс е дете на принципите на абстракцията и полиморфизма. Интерфейсът е много подобен на абстрактен клас, в който всички методи са абстрактни. Декларира се по същия начин като клас, но използваме ключовата interface
дума.
interface Feline
{
void purr();
void meow();
void growl();
}
Ето някои полезни факти за интерфейсите:
1. Деклариране на интерфейс
interface Drawable
{
void draw();
}
interface HasValue
{
int getValue();
}
- Вместо ключовата
class
дума пишемinterface
. - Съдържа само абстрактни методи (не пишете
abstract
ключовата дума) - Всъщност интерфейсите имат всички
public
методи
Един интерфейс може да наследява само интерфейси. Но един интерфейс може да има много родители. Друг начин да се каже това е да се каже, че Java има множествено наследяване на интерфейси. Примери:
interface Piece extends Drawable, HasValue
{
int getX();
int getY();
}
3. Наследяване на класове от интерфейси
Един клас може да наследява множество интерфейси (само от един клас). Това става с помощта на implements
ключовата дума. Пример:
abstract class ChessItem implements Drawable, HasValue
{
private int x, y, value;
public int getValue()
{
return value;
}
public int getX()
{
return x;
}
public int getY()
{
return y;
}
}
Класът ChessItem е деклариран абстрактен: той имплементира всички наследени методи с изключение на draw
. С други думи, ChessItem
класът съдържа един абстрактен метод — draw()
.
Техническото meaning на ключовите думи extends
и implements
е същото: и двете са наследство. Разграничението е напequalsо, за да се подобри четливостта на codeа. Казваме също, че класовете се наследяват (чрез extends
) и интерфейсите се изпълняват (чрез implements
)
4. Променливи
Ето най-важното: обикновените променливи не могат да бъдат декларирани в интерфейси (въпреки че статичните могат).
Но защо се нуждаем от интерфейси? Кога се използват? Интерфейсите имат две силни предимства пред класовете:
2. Разделяне на "описанието на методите" от тяхното изпълнение.
По-рано казахме, че ако искате да разрешите методите на вашия клас да бъдат извиквани от други класове, тогава вашите методи трябва да бъдат маркирани с ключовата public
дума. Ако искате някои от тези методи да се извикват само от вашия клас, трябва да ги маркирате с ключовата private
дума. С други думи, ние разделяме методите на класа на две категории: "за всички" и "само за наша собствена употреба".
Интерфейсите спомагат за по-нататъшното укрепване на това разделение. Ще направим специален „клас за всеки да използва“, Howто и втори клас „само за наша собствена употреба“, който ще наследи първия клас. Ето How би изглеждало приблизително:
Преди | След |
---|---|
|
|
|
|
Разделяме нашия клас на две: интерфейс и клас , който наследява интерфейса . И Howво е предимството тук?
Много различни класове могат да имплементират (наследяват) един и същ интерфейс. И всеки може да има собствено поведение. Например ArrayList
LinkedList
има две различни реализации на List
интерфейса.
Така скриваме не само различните имплементации, но и самия имплементиращ клас (тъй като имаме нужда само от интерфейса в codeа). Това ни позволява да бъдем много гъвкави: докато програмата се изпълнява, можем да заменим един обект с друг, променяйки поведението на обект, без да засягаме всички класове, които го използват.
Това е много мощна техника, когато се комбинира с полиморфизъм. Засега далеч не е очевидно защо трябва да правите това. Първо трябва да се сблъскате с програми с десетки or стотици класове, за да разберете, че интерфейсите могат да направят живота ви много по-лесен, отколкото без тях.
3. Множествено наследяване
В Java всички класове могат да имат само един родителски клас. В други езици за програмиране класовете често могат да имат множество родителски класове. Това е много удобно, но носи и много проблеми.
Създателите на Java стигнаха до компромис: те забраниха множественото наследяване на класове, но позволиха множественото наследяване на интерфейси. Един интерфейс може да има множество родителски интерфейси. Един клас може да има множество родителски интерфейси, но само един родителски клас.
Защо забраниха множественото наследяване на класове, но позволиха множествено наследяване на интерфейси? Поради така наречения проблем с наследяването на диаманти:

Когато клас B наследи клас A, той не знае нищо за класовете C и D. Така че използва променливите от клас А, Howто намери за добре. Класът C прави същото: той използва променливите на клас A, но по различен начин. И всичко това води до конфликт в D класа.
Нека разгледаме следния прост пример. Да кажем, че имаме 3 класа:
class Data
{
protected int value;
}
class XCoordinate extends Data
{
public void setX (int x) { value = x;}
public int getX () { return value;}
}
class YCoordinate extends Data
{
public void setY (int y) { value = y;}
public int getY () { return value; }
}
Класът Data съхранява value
променливата. Неговият клас наследник XCoordinate използва тази променлива за съхраняване на x
стойността, а YCoordinate
класът потомък я използва, за да съхранява y
стойността.
И работи. Отделно. Но ако искаме класът XYCoordinates да наследи и двата XCoordinate
класа YCoordinate
, тогава ще получим счупен code. Този клас ще има методите на своите предшественици, но те няма да работят правилно, защото имат същите value variable
.
Но тъй като интерфейсите не могат да имат променливи, те не могат да имат този вид конфликт. Съответно се допуска множествено наследяване на интерфейси.
GO TO FULL VERSION