JavaRush /Java Blog /Random-KO /“관심은 많지만 아무것도 이해하지 못하는” 사람들을 위한 인터페이스
Lilly
레벨 25
Москва

“관심은 많지만 아무것도 이해하지 못하는” 사람들을 위한 인터페이스

Random-KO 그룹에 게시되었습니다
안녕하세요 여러분! 이 게시물은 레벨 12(Java Core의 두 번째 레벨)에서 인터페이스에 대한 여러 문제를 이미 보고 심지어 해결했지만 여전히 다음과 같이 궁금해하는 사람들을 위해 작성되었습니다. "그래서 인터페이스에 메소드를 구현해야 하는데 왜 필요한가요? ?”가 인터페이스에 선언되어 있나요??? 즉, 코드의 양이 줄어들지 않습니다!!!" . 이 주제는 후속 강의에서 다루어질 것이며 인터페이스 사용에 대한 더 깊은 의미와 옵션이 공개될 것이라는 점에는 의심의 여지가 없습니다. 하지만 완전히 불안하시다면 환영합니다. 처음에는 댓글을 쓰고 싶었지만, 댓글이 너무 길어질 것 같아서 여기까지입니다. 이것은 나의 첫 번째 기사이므로 합리적인 비판을 환영합니다. 내가 어딘가에 틀렸다면 자유롭게 정정해 주세요. 따라서 우리가 알고 있듯이 자체 클래스를 만들 때 하나 의 추상 또는 비추상 클래스 에서만 상속 할 수 있습니다 . 그러나 동시에 우리 수업에서는 상당히 많은 수 의 인터페이스를 구현할 수 있습니다 . 상속할 때 상식에 따라 관련 개체를 후손에 기록해야 합니다 (불도저에서 고양이를 상속받지 않으며 그 반대도 마찬가지입니다). 인터페이스 의 경우 논리는 약간 다릅니다. 여기서 중요한 것은 "엔티티" 자체가 아니라 해당 "기술" 또는 이를 통해 수행할 수 있는 작업입니다 . 예를 들어, 고양이와 불도저는 모두 우주에서 이동할 수 있습니다. 즉, 원칙적으로 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 +
                           " отсюда на четырёх копытах");
    }
}
및 나무늘보( Sloth ):
// Ленивец НАСЛЕДУЕТ класс 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()“관심은 많지만 아무것도 이해하지 못하는” 분들을 위한 인터페이스 - 1 메서드 에서 표준 메시지를 표시합니다. 클래스 Unit , 나무늘보의 move( 메소드)가 재정의 되었습니다 . 그러나 추상 클래스는 특정 단위가 "할 수 있는" 작업을 찾는 데 도움이 되지 않습니다. 이것이 바로 인터페이스가 작동하는 곳입니다 . instanceof를 사용하여 이 유닛이 특정 작업을 수행할 수 있는지( 필요한 인터페이스를 지원하는지 여부 ) 확인하고 , 그렇다면 ((CanFly) 유닛)을 사용하는 등 이미 우리에게 익숙한 유형 캐스팅을 사용합니다. fly() Unit 유형 객체를 CanFly 인터페이스 유형 으로 캐스팅 하고 해당 fly() 메서드를 호출합니다 . 그리고 우리 물체의 클래스가 무엇인지는 중요하지 않습니다. 유일하게 중요한 것은 그가 "이력서"에 비행 능력을 표시했다는 것입니다. 우리는 그에게 말합니다: "당신은 방법을 알고 있으니 잘 하세요! 우리는 당신이 어떻게 하든 상관하지 않습니다 . " 즉, 개발자인 우리에게 이는 CanFly 인터페이스를 구현하는 클래스가 언제든지 어떤 방식으로든 fly() 메서드의 구현을 변경할 수 있음을 의미합니다 . 이를 구현하는 새 클래스가 나타날 수 있으며, 반대로 개발자는 다음과 같은 작업을 수행할 수 있습니다. 오래된 클래스 중 일부를 제거하십시오. 그러나 이 메서드가 명시된 기능을 수행하고 개체가 날아다니는 한 우리는 상관하지 않습니다. 이 인터페이스를 구현하는 객체와 함께 작동하는 코드는 동일하게 유지되므로 아무것도 변경할 필요가 없습니다. 두 번째 주기 이후 모두가 탈출을 시도할 때 홍수 규모에 따라 화면에 인터페이스가 없으면 각 객체가 특정 클래스를 준수하는지 확인해야 합니다(그리고 우리는 모든 것을 확인하려면) 각 특정 클래스의 기술을 염두에 두십시오. 즉, 지금 우리 앞에는 어린 양이 있고 그 양은 달릴 줄 아는 것 같습니다. 그러한 문자의 다양한 유형(클래스)이 수십 또는 수백 개 있다면 어떻게 될까요? 그리고 그것을 작성한 사람이 당신이 아니라 다른 프로그래머여서 누가 무엇을 할 수 있는지 전혀 모른다면 어떨까요? 훨씬 더 어려울 것입니다... 출판 후 약간의 추가 사항:“관심은 많지만 아무것도 이해하지 못하는” 분들을 위한 인터페이스 - 2“관심은 많지만 아무것도 이해하지 못하는” 사람들을 위한 인터페이스 - 3 실생활에서 당신은 프로젝트에 참여하는 유일한 사람이 아닙니다. 당신이 논리만 한다고 가정해보자. 당신이 상호작용하는 모든 객체는 다른 프로그래머가 작성한 것입니다. 코드가 작동하는 모든 클래스를 알지 못할 수도 있습니다. 당신이 그들에게 필요한 것은 그들이 당신에게 필요한 일을 하는 것뿐입니다. 그러나 그들은 모두 완전히 다른 방식으로 이를 수행할 수 있습니다. 하지만 코드에서 특정 인터페이스를 지원하는 클래스의 객체에만 작동하는 메서드를 생성한다고 가정해 보겠습니다.
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
이것이 바로 인터페이스가 유용할 수 있는 방법입니다. 그리고 이것이 그들의 기능과 적용 방법의 전부는 아닙니다. 나중에 강좌에서 더 자세히 배우게 될 것입니다 =) 끝까지 읽어주셔서 감사합니다 =)
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION