大家好!相反,這篇文章是為那些已經看到甚至解決了 12 級(Java 核心 2 級)介面上的幾個問題但仍然想知道的人寫的:「那麼,如果您仍然需要在其中實現方法,為什麼還需要它們呢?” ? ”在接口中聲明???也就是說,它們並沒有減少代碼量!!!” 。我毫不懷疑這個主題將在後續的講座中涵蓋,並將揭示使用介面的更深層次的含義和選項,但如果您完全感到不安,那麼不客氣。最初我想寫一條評論,但我意識到這將是一個很長的評論,所以我們就在這裡。這是我的第一篇文章,歡迎合理批評。如果我有什麼地方說錯了,請隨時糾正我。因此,我們知道,在創建自己的類別時,我們只能繼承一個抽象類別或非抽象類別。但同時,在我們的類別中我們可以實作相當多的 介面。繼承時,您需要以常識為指導,並且仍然將相關實體寫到您的後代中(我們不會從推土機繼承貓,反之亦然)。對於接口,邏輯有點不同:這裡重要的不是「實體」本身,而是它們的「技能」或可以用它們做什麼。例如,貓和推土機都可以在太空中移動。也就是說,原則上他們可以實作CanMove或Moveable介面。或者一個人和一隻貓。他們都會喝水,但方式不同:一個人用杯子喝茶或咖啡,而貓則從碗裡喝一些水或牛奶。但總的來說,他們都喝酒,所以他們每個人都可以自己實作 CanDrink 介面。這對我們有什麼好處呢?想像一下您正在製作一款遊戲。假設您有一個位置:一條河,兩岸都有河岸,然後是森林和山脈。岸邊棲息著各種生物。突然洪水逼近。凡會飛的人都會飛走。那些不能飛但能跑的人就跑吧。有人知道如何游泳,所以原則上他們不關心你的洪水(好吧,或者他們可以游上岸),儘管他們中的一些人可能會首先嘗試逃跑(如果他們知道如何)。其餘的人,儘管可能很悲傷,也會死。讓我們嘗試實現這一點。(不要被一次大量的程式碼嚇到=)大部分都是註解)我們可能需要什麼類別?讓我們從不同角色或單位的抽象類別開始(抱歉,我不擅長遊戲術語,如果我錯了,請糾正我)。讓它成為Unit類別。我們假設絕對所有單位都可以在空間中移動並發出聲音。抽象類別單位:
// Абстрактный класс для всех юнитов
public static abstract class Unit {
// Базовый метод движения.
// По умолчанию (если не переопределено) будет у всех наследников.
public void move (int x, int y) {
System.out.println("Я ( " + getClassName() + " ) просто брожу по полю на " +
x + " метров вправо и " + y + " метров вперед");
}
// Абстрактный метод, который ДОЛЖЕН ПЕРЕОПРЕДЕЛИТЬ у себя
// КАЖДЫЙ КЛАСС, унаследованный от Unit.
public abstract void makeSound();
// Вспомогательный метод получения имени класса
// без всей лишней информации.
public String getClassName() {
return this.getClass().getSimpleName();
}
}
我們的單位需要 什麼介面(「技能」)?他們可以跑(CanRun)、游泳(CanSwim)或飛(CanFly)。有些人可能同時擁有多種技能,有些不幸的人可能什麼都沒有。
// Интерфейсы. КАЖДЫЙ КЛАСС, "наследующий" Howой-то интерфейс,
// ДОЛЖЕН РЕАЛИЗОВАТЬ его у себя.
interface CanRun {
void run(String action);
}
interface CanSwim {
void swim();
}
interface CanFly {
void fly();
}
接下來我們建立從抽象 Unit 類別繼承的類別。該路徑將是 Human類:
// Человек НАСЛЕДУЕТ абстрактный класс Unit,
// а также РЕАЛИЗУЕТ интерфейсы CanRun, CanSwim
public static class Human extends Unit implements CanRun, CanSwim {
// Переопределяем метод public void makeSound()
// родительского абстрактного класса Unit
@Override
public void makeSound() {
System.out.print("Йу-хуу! ");
}
// РЕАЛИЗУЕМ метод public void run(String action) интерфейса CanRun
@Override
public void run(String action) {
System.out.println("Я ( " + this.getClassName() + " ) " + action +
" отсюда на двух ногах ");
}
// РЕАЛИЗУЕМ метод public void swim() интерфейса CanSwim
@Override
public void swim() {
System.out.println("Я ( " + this.getClassName() + " ) " +
"с двумя руками и двумя ногами " +
"хорошо плаваю");
}
}
鳥類。_ 是的,我知道沒有簡單的鳥,但為了簡單起見,就讓它只是一隻鳥,我們不要把它抽象化:
// Птица НАСЛЕДУЕТ абстрактный класс Unit,
// и РЕАЛИЗУЕТ интерфейс CanFly
public static class Bird extends Unit implements CanFly {
// Переопределяем абстрактный метод public void makeSound()
// родительского абстрактного класса Unit
@Override
public void makeSound() {
System.out.print("Курлык! ");
}
// РЕАЛИЗУЕМ метод public void fly() интерфейса CanFly
@Override
public void fly() {
System.out.println("Я ( " + this.getClassName() + " ) улетел отсюда");
}
}
現在讓我們建立另一個抽象類別 Animals ,它將繼承自Unit:
// Абстрактный класс Животных, НАСЛЕДУЕТ абстрактный класс Unit
public static abstract class Animal extends Unit {
// тут могут быть Howие-то данные и/or методы
}
已經是他的繼承人羔羊(綿羊):
// Баран НАСЛЕДУЕТ класс Animal,
// и РЕАЛИЗУЕТ интерфейс CanRun
public static class Sheep extends Animal implements CanRun {
// Переопределяем абстрактный метод public void makeSound()
// родительского абстрактного класса Unit
@Override
public void makeSound() {
System.out.print("Беееее! ");
}
// РЕАЛИЗУЕМ метод public void run(String action) интерфейса CanRun
@Override
public void run(String action) {
System.out.println("Я ( "+ this.getClassName() + " ) " + action +
" отсюда на четырёх копытах");
}
}
和樹懶(樹懶):
// Ленивец НАСЛЕДУЕТ класс Animal
// и внутри себя ПЕРЕОПРЕДЕЛЯЕТ метод
// void move(int x, int y) абстрактного класса Unit
public static class Sloth extends Animal {
// Переопределяем абстрактный метод public void makeSound()
// родительского абстрактного класса Unit
@Override
public void makeSound() {
System.out.print("Зевает... ");
}
// Переопределяем метод public void move(int x, int y)
// родительского абстрактного класса Unit
@Override
public void move(int x, int y) {
System.out.println("Я ( "+ getClassName() + " ) очень медленный, поэтому " +
"переместился на " + (int)(x/12) + " вправо " +
"и на " + (int)(y/12) + " вперед");
}
}
抽象 Unit 類別的move(int x, int y) 方法不是抽象的,因此我們不必重寫它,但我們確實讓 Sloth 的速度變慢了一些。現在讓我們繼續行動。
public static void main(String[] args) {
// создаём список юнитов
// и добавляем туда представителей разных классов
List<Unit> units = new ArrayList<unit>();
units.add(new Human());
units.add(new Bird());
units.add(new Sheep());
units.add(new Sloth());
// проходим в цикле по всем юнитам и вызываем метод move(int x, int y).
// У всех, кроме ленивца, будет вызван метод базового класса.
// У Ленивца будет вызван его переопределенный метод
for (Unit unit : units) {
// в самом начале ничего не происходит, юниты просто двигаются.
unit.move((int)(Math.random()*50), (int)(Math.random()*50));
}
System.out.println("\n...Наводнение приближается....");
int distanceOfFlood = (int)(Math.random()*1000); // Число от 0 до 1000
System.out.println("...Наводнение на " + distanceOfFlood + " от берега...\n");
// проходим в цикле по всем юнитам
for (Unit unit : units) {
unit.makeSound(); // у каждого юнита свой, переопределенный, звук
// смотрим, кто что "умеет":
// если юнит умеет летать
if(unit instanceof CanFly)
((CanFly) unit).fly(); // проблем нет, улетает
// если юнит не умеет летать, но умеет бегать
else if(unit instanceof CanRun) {
// смотрим на Howое расстояние разлилась вода
if(distanceOfFlood < 400) {
// если меньше 400 (условно метров)
((CanRun) unit).run("убежал"); // юнит убежал
}
else {
// если больше 400, юнит безуспешно пытается убежать
((CanRun) unit).run("пытался убежать");
// если юнит может не только бегать, но и плавать
if (unit instanceof CanSwim) {
((CanSwim) unit).swim(); // плывёт
}
// иначе умирает (он хотя бы пытался)
else unitIsDead(unit);
}
}
// если юнит не летает, не бегает, но может плавать
else if (unit instanceof CanSwim)
((CanSwim) unit).swim(); // плывёт
else
// если юнит не умеет совсем ничего - грустненько :(
unitIsDead(unit); // умирает
System.out.println();
}
}
public static void unitIsDead(Unit unit) {
System.out.println("Извините, я ( " + unit.getClassName() + " ) умер");
}
數位文字 12、50、400 和 1000 是暫時取得的;其他可以指定,但我希望邏輯是清晰的。因此,正如我們所看到的,擁有一個具有通用方法的抽象父類,我們不必考慮這個或那個單元是什麼特定的類,而只需調用這些方法( makeSound() 和 move ( ) )。在循環的第一遍之後,當在所有單元上呼叫move()方法時,螢幕上將顯示以下內容: 顯然,除了樹懶之外的所有物件都顯示來自抽象父級的move()方法的 標準訊息類別Unit,且樹懶有一個move( 方法 )已被重新定義。然而,抽象類別並不能幫助我們找出特定單元「可以做什麼」。這就是接口發揮作用的地方。使用instanceof,我們找出這個單元是否可以執行某些操作(是否支援我們需要的介面),如果可以,我們使用我們已經熟悉的類型轉換,例如,使用((CanFly)單元)。在Fly() 方法中,我們將Unit類型物件轉換為CanFly介面類型,並呼叫其Fly()方法。我們的對像是什麼級別並不重要,唯一重要的是他在“簡歷”中表明了飛行的能力。我們告訴他:“你知道怎麼做,所以飛吧!我們不在乎你怎麼做。 ” 也就是說,對於我們作為開發人員來說,這意味著實現CanFly介面的類別可以隨時以任何方式更改Fly()方法的實現,實現它的新類別可能會出現,或者相反,開發人員可以刪除一些舊的類。但只要這個方法執行其規定的功能並且物體會飛,我們就不在乎。我們與實現此介面的物件一起使用的程式碼將保持不變;我們不必更改任何內容。在第二個週期之後,當每個人都試圖逃離時,根據洪水的規模,我們將在螢幕上看到或 沒有 介面,我們將不得不檢查每個物件是否符合某個類別(並且我們將有檢查所有內容)並記住每個特定類別的技能。就是現在我們面前有一隻小羊,它似乎知道如何奔跑。如果您有幾十個或數百個不同類型(類別)的此類字元怎麼辦?如果它們不是你寫的,而是由另一個程式設計師寫的,那你不知道誰能做什麼?這會困難得多...... 出版後有一個小小的補充: 在現實生活中,您並不是唯一一個從事專案的人。假設你只做邏輯。您與之互動的所有物件都是由其他程式設計師編寫的。您甚至可能不知道您的程式碼所使用的所有類別。您需要他們做的就是他們做您需要他們做的事情。然而,他們都可以以完全不同的方式做到這一點。但是,假設您在程式碼中建立了一個方法,該方法僅適用於支援特定介面的類別的對象
void doSomething(CanFly f)
也就是說,您使用方法參數設定介面。不是一個具體的類,而是一個介面。所有其他程式設計師在呼叫您的 void doSomething(CanFly) 方法時,必須將實作 CanFly 的類別的明確物件或可以強制轉換為它的某個類別的某個物件作為參數傳遞:
Object obj = new Bird();
doSomething(obj); // ошибка компиляции Object cannot be converted to CanFly
doSomething((CanFly) obj); // нет ошибки, потому что obj у нас класса Bird и реализует CanFly
Bird b = new Bird();
doSomething(b); // нет ошибки
Human h = new Human() ;
doSomething(h); // ошибка компиляции
doSomething((CanFly) h); // ошибка времени выполнения ClassCastException
這就是接口的用處。而這還不是他們的全部能力和應用方法。我們可能會在課程稍後學到更多 =) 感謝您讀到最後 =)
GO TO FULL VERSION