JavaRush /Blog Java /Random-ES /Interfaces para aquellos que están “muy interesados, pero...
Lilly
Nivel 25
Москва

Interfaces para aquellos que están “muy interesados, pero no entienden nada”

Publicado en el grupo Random-ES
¡Hola a todos! Esta publicación fue escrita, más bien, para aquellos que ya han visto e incluso resuelto varios problemas sobre interfaces en el nivel 12 (segundo nivel de Java Core), pero aún se preguntan: “Entonces, ¿por qué son necesarios si aún es necesario implementar métodos en ellos? ?” declarado en la interfaz??? Es decir, ¡¡¡no reducen la cantidad de código!!!" . No tengo ninguna duda de que este tema se tratará en conferencias posteriores y se revelarán significados y opciones más profundos para el uso de interfaces, pero si está completamente incómodo, de nada. Inicialmente quería escribir un comentario, pero me di cuenta que sería un comentario muy largo, así que aquí estamos. Este es mi primer artículo, acepto críticas razonables. Si me equivoco en algún punto, no dudes en corregirme. Entonces, como sabemos, al crear nuestra propia clase, solo podemos heredar de una clase abstracta o no abstracta . Pero al mismo tiempo, en nuestra clase podemos implementar una cantidad bastante grande de interfaces . Al heredar, debes guiarte por el sentido común y aun así anotar entidades relacionadas entre tus descendientes (no heredaremos un gato de una excavadora y viceversa). Con las interfaces , la lógica es un poco diferente: lo importante aquí no son las “entidades” en sí mismas, sino sus “habilidades” o lo que se puede hacer con ellas . Por ejemplo, tanto un gato como una excavadora pueden moverse en el espacio. Es decir, en principio, pueden implementar la interfaz CanMove o Moveable. O un hombre y un gato. Ambos pueden beber, pero lo hacen de diferentes maneras: una persona bebe té o café en una taza y un gato bebe un poco de agua o leche de un cuenco. Pero, en general, ambos beben, por lo que cada uno puede realizar su propia implementación de la interfaz CanDrink. ¿Qué beneficio tiene esto para nosotros? Imagina que estás creando un juego. Digamos que tienes una ubicación: un río, con orillas a ambos lados, y luego un bosque y montañas. En la orilla descansan varios tipos de seres vivos. De repente se acerca una inundación. Todo el que sabe volar se va volando. Aquellos que no pueden volar, pero sí correr, corren. Alguien sabe nadar, por lo que, en principio, no le importa su inundación (bueno, o puede nadar hasta la orilla), aunque algunos de ellos pueden intentar escapar primero (si saben cómo). El resto, por triste que sea, morirá. Intentemos implementar esto. (No te asustes por la gran cantidad de código a la vez =) La mayoría son comentarios) ¿Qué clases podríamos necesitar? Comencemos con una clase abstracta de diferentes personajes o unidades (lo siento, no soy bueno en terminología de juegos, corríjanme si me equivoco). Sea esta la clase Unit . Supondremos que absolutamente todas las unidades pueden moverse en el espacio y emitir sonidos. Unidad de clase abstracta :
// Абстрактный класс для всех юнитов
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();
    }
}
¿ Qué interfaces (“habilidades”) necesitan nuestras unidades? Pueden correr ( CanRun ), nadar ( CanSwim ) o volar ( CanFly ). Algunas personas pueden tener varias habilidades a la vez, mientras que otras desafortunadas pueden no tener ninguna.
// Интерфейсы. КАЖДЫЙ КЛАСС, "наследующий" Cómoой-то интерфейс,
// ДОЛЖЕН РЕАЛИЗОВАТЬ его у себя.
interface CanRun {
    void run(String action);
}
interface CanSwim {
    void swim();
}
interface CanFly {
    void fly();
}
A continuación creamos clases que heredan de la clase abstracta Unit. El camino será la clase Humana :
// Человек НАСЛЕДУЕТ абстрактный класс 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() + " ) " +
                           "с двумя руками и двумя ногами " +
                           "хорошо плаваю");
    }
}
Clase de aves . Sí, entiendo que no existen pájaros simples, pero por simplicidad, que sea solo un pájaro, no lo hagamos abstracto:
// Птица НАСЛЕДУЕТ абстрактный класс 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() + " ) улетел отсюда");
    }
}
Ahora creemos otra clase abstracta Animales , que se heredará de Unidad :
// Абстрактный класс Животных, НАСЛЕДУЕТ абстрактный класс Unit
public static abstract class Animal extends Unit {
    // тут могут быть Cómoие-то данные и/o методы
}
Y ya sus herederos Cordero ( Oveja ):
// Баран НАСЛЕДУЕТ класс 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 +
                           " отсюда на четырёх копытах");
    }
}
y Pereza ( Pereza ):
// Ленивец НАСЛЕДУЕТ класс 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) + " вперед");
    }
}
El método move(int x, int y) de la clase abstracta Unit no es abstracto , por lo que no tenemos que anularlo, pero ralentizamos un poco las cosas para Sloth. Ahora pasemos a la acción.
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) {
			// смотрим на Cómoое расстояние разлилась вода
            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() + " ) умер");
}
Los literales numéricos 12, 50, 400 y 1000 se toman de improviso; se pueden especificar otros, pero espero que la lógica sea clara. Entonces, como podemos ver, al tener una clase padre abstracta con métodos generales, no tenemos que pensar en qué clase específica es esta o aquella unidad, sino que simplemente llamamos a estos métodos ( makeSound() y move ( ) ) . Después del primer paso en el bucle, cuando se llama al método move() en todas las unidades , se mostrará lo siguiente en la pantalla: Interfaces para aquellos que están “muy interesados, pero no entienden nada” - 1 Obviamente, todos los objetos excepto el perezoso muestran un mensaje estándar del método move() del padre abstracto. Unidad de clase , y el perezoso tiene un movimiento (método) que ha sido redefinido . Sin embargo, una clase abstracta no nos ayudará a descubrir qué "puede hacer" una unidad en particular. Aquí es donde entran en juego las interfaces . Usando instancia de , averiguamos si esta unidad puede realizar ciertas acciones ( si es compatible con la interfaz que necesitamos ) y, de ser así, usamos el tipo de conversión que ya nos resulta familiar, por ejemplo, usando (unidad (CanFly)). fly() convertimos nuestro objeto de tipo Unidad al tipo de interfaz CanFly y llamamos a su método fly() . Y no importa qué clase tenga nuestro objeto, lo único importante es que indicó la capacidad de volar en su “currículum”. Le decimos: "Ya sabes cómo, ¡así que vuela! No nos importa cómo lo hagas " . Es decir, para nosotros, como desarrolladores, esto significa que las clases que implementan la interfaz CanFly pueden cambiar la implementación del método fly() en cualquier momento y de cualquier manera , pueden aparecer nuevas clases que lo implementen o, por el contrario, los desarrolladores pueden eliminar algunas de las clases antiguas. Pero mientras este método realice las funciones indicadas y el objeto vuele, no nos importa. Nuestro código que trabaja con objetos que implementan esta interfaz seguirá siendo el mismo; no tendremos que cambiar nada. Después del segundo ciclo, cuando todos estén tratando de escapar, dependiendo de la escala de la inundación, veremos en la pantalla o Interfaces para aquellos que están “muy interesados, pero no entienden nada” - 2 Sin Interfaces para aquellos que están “muy interesados, pero no entienden nada” - 3 una interfaz, tendríamos que verificar que cada objeto cumpla con una determinada clase (y tendríamos para comprobarlo todo) y tener en cuenta las habilidades de cada clase específica. Es decir, ahora hay un cordero frente a nosotros y parece saber correr. ¿Qué pasa si tienes varias docenas o cientos de tipos (clases) diferentes de dichos personajes? ¿Y si no fuiste tú quien los escribió, sino otro programador, y no tienes idea de quién puede hacer qué? Sería mucho más difícil... Y un pequeño añadido tras la publicación: En la vida real, no eres el único que trabaja en un proyecto. Digamos que sólo haces lógica. Todos los objetos con los que interactúas están escritos por otros programadores. Es posible que ni siquiera conozcas todas las clases con las que trabaja tu código. Todo lo que necesitas de ellos es que hagan lo que tú necesitas que hagan. Sin embargo, todos pueden hacer esto de maneras completamente diferentes. Pero, digamos, en tu código creas un método que funciona solo con objetos de clases que soportan una determinada interfaz.
void doSomething(CanFly f)
es decir, configura la interfaz utilizando el parámetro método. No es una clase concreta, sino una interfaz. Y todos los demás programadores, al llamar a este método void doSomething(CanFly) suyo, deben pasar como argumentos un objeto explícito de una clase que implementa CanFly, o algún objeto de alguna clase que se pueda convertir en él:
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
Así es como las interfaces pueden resultar útiles. Y estas no son todas sus capacidades y métodos de aplicación. Probablemente aprenderemos más más adelante en el curso =) Gracias por leer hasta el final =)
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION