大家好!相反,这篇文章是为那些已经看到甚至解决了 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