こんにちは、みんな!この記事はむしろ、レベル 12 (Java コアの第 2 レベル) のインターフェースに関するいくつかの問題をすでに見て解決したことがあるが、まだ次のように疑問に思っている人向けに書かれています。 ?” インターフェイスで宣言されています??? つまり、コードの量は減りません!!!" 。このトピックについては今後の講義で取り上げられ、インターフェイスのより深い意味や使用方法が明らかになることは間違いありませんが、まったく不安な方も大歓迎です。本当はコメントを書こうと思ったのですが、長くなりそうなのでこの辺にさせていただきます。これは私の最初の記事です。合理的な批判は歓迎します。どこか間違っている場合は、遠慮なく修正してください。したがって、ご存知のとおり、独自のクラスを作成するときは、 1 つの抽象クラスまたは非抽象クラスからのみ継承できます。しかし同時に、私たちのクラスではかなり多くのインターフェイスを実装できます。継承するときは、常識に従って、関連するエンティティを子孫に書き込む必要があります(ブルドーザーから猫を継承することはありませんし、その逆も同様です)。インターフェイスの場合、ロジックは少し異なります。ここで重要なのは、「エンティティ」自体ではなく、その「スキル」、またはそれを使って何ができるかです。たとえば、猫もブルドーザーも空間を移動できます。つまり、原則として、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() + " ) улетел отсюда");
}
}
次に、 Unitから継承される別の抽象クラス Animals を作成しましょう。
// Абстрактный класс Животных, НАСЛЕДУЕТ абстрактный класс 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) メソッドはabstract ではないため、オーバーライドする必要はありませんが、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()メソッドの実装を変更できることを意味し、それを実装する新しいクラスが出現したり、逆に開発者がそのメソッドを変更したりできることを意味します。古いクラスのいくつかを削除します。しかし、このメソッドが指定された機能を実行し、オブジェクトが飛行する限り、気にする必要はありません。このインターフェースを実装するオブジェクトを操作するコードは同じままであり、何も変更する必要はありません。2 番目のサイクルの後、洪水の規模に応じて、全員が避難しようとすると、画面に次のいずれかが表示されます。インターフェイスがなければ、各オブジェクトが 特定のクラスに準拠している かどう かを確認する必要があります (そして、すべてをチェックして)、それぞれの特定のクラスのスキルを念頭に置いてください。つまり、今私たちの前に子羊がいて、走り方を知っているようです。このような文字の異なるタイプ (クラス) が数十または数百ある場合はどうなるでしょうか? そして、それらを書いたのがあなたではなく、別のプログラマーで、誰が何をできるのか全く分からないとしたらどうでしょうか? それはもっと難しいでしょう... そして、出版後の小さな追加: 現実の世界では、プロジェクトに取り組んでいるのはあなただけではありません。ロジックだけを行うとしましょう。あなたが操作するすべてのオブジェクトは、他のプログラマーによって書かれています。コードが動作するすべてのクラスを知っているわけではないかもしれません。あなたが彼らに必要なのは、彼らがあなたがしてほしいことをやってくれることだけです。ただし、これらはすべてまったく異なる方法でこれを実行できます。ただし、コード内で、特定のインターフェイスをサポートするクラスのオブジェクトでのみ動作するメソッドを作成したとします。
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