JavaRush /Blogue Java /Random-PT /Interfaces para quem está “muito interessado, mas não ent...
Lilly
Nível 25
Москва

Interfaces para quem está “muito interessado, mas não entende nada”

Publicado no grupo Random-PT
Olá a todos! Este post foi escrito, sim, para quem já viu e até resolveu diversos problemas em Interfaces no nível 12 (2º nível do Java Core), mas ainda se pergunta: “Então por que eles são necessários se você ainda precisa implementar métodos neles ?” declarado na interface??? Ou seja, não reduzem a quantidade de código!!!" . Não tenho dúvidas de que este tópico será abordado em palestras subsequentes, e significados mais profundos e opções de uso de interfaces serão revelados, mas se você estiver completamente inquieto, seja bem-vindo. Inicialmente queria escrever um comentário, mas percebi que seria um comentário muito longo, então aqui estamos. Este é meu primeiro artigo, aceito críticas razoáveis. Se eu estiver errado em algum lugar, sinta-se à vontade para me corrigir. Então, como sabemos, ao criar nossa própria classe, podemos herdar apenas de uma classe abstrata ou não abstrata . Mas, ao mesmo tempo, podemos implementar um número bastante grande de interfaces em nossa classe . Ao herdar, você precisa ser guiado pelo bom senso e ainda anotar entidades relacionadas em seus descendentes (não herdaremos um gato de uma escavadeira e vice-versa). Com interfaces , a lógica é um pouco diferente: o que importa aqui não são as “entidades” em si, mas suas “habilidades” ou o que pode ser feito com elas . Por exemplo, tanto um gato quanto uma escavadeira podem se mover no espaço. Ou seja, em princípio, podem implementar a interface CanMove ou Moveable. Ou um homem e um gato. Ambos podem beber, mas o fazem de maneiras diferentes: uma pessoa bebe chá ou café em uma xícara e um gato bebe um pouco de água ou leite em uma tigela. Mas, em geral, ambos bebem, então cada um pode fazer sua própria implementação da interface CanDrink. Que benefício isso traz para nós? Imagine que você está fazendo um jogo. Digamos que você tenha um local: um rio, com margens em ambos os lados, e depois uma floresta e montanhas. Vários tipos de criaturas vivas repousam na costa. De repente, uma inundação se aproxima. Todo mundo que pode voar voa para longe. Aqueles que não podem voar, mas podem correr, corram. Alguém sabe nadar, então, em princípio, eles não se importam com a enchente (bem, ou podem nadar até a costa), embora alguns deles possam primeiro tentar escapar (se souberem). O resto, por mais triste que seja, morrerá. Vamos tentar implementar isso. (Não se assuste com a grande quantidade de código de uma só vez =) A maior parte são comentários) De quais classes podemos precisar? Vamos começar com uma classe abstrata de diferentes personagens ou unidades (desculpe, não sou forte em terminologia de jogos, corrija-me se estiver errado). Deixe esta ser a classe Unit . Assumiremos que absolutamente todas as unidades podem se mover no espaço e emitir sons. Unidade de classe abstrata :
// Абстрактный класс для всех юнитов
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();
    }
}
De quais interfaces (“habilidades”) nossas unidades precisam? Eles podem correr ( CanRun ), nadar ( CanSwim ) ou voar ( CanFly ). Algumas pessoas podem ter várias habilidades ao mesmo tempo, enquanto algumas pessoas infelizes podem não ter nenhuma.
// Интерфейсы. КАЖДЫЙ КЛАСС, "наследующий" Howой-то интерфейс,
// ДОЛЖЕН РЕАЛИЗОВАТЬ его у себя.
interface CanRun {
    void run(String action);
}
interface CanSwim {
    void swim();
}
interface CanFly {
    void fly();
}
A seguir, criamos classes que herdam da classe abstrata Unit. O caminho será a classe 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() + " ) " +
                           "с двумя руками и двумя ногами " +
                           "хорошо плаваю");
    }
}
Aula de pássaros . Sim, entendo que não existem pássaros simples, mas por uma questão de simplicidade, que seja apenas um pássaro, não vamos torná-lo abstrato:
// Птица НАСЛЕДУЕТ абстрактный класс 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() + " ) улетел отсюда");
    }
}
Agora vamos criar outra classe abstrata Animals , que será herdada de Unit :
// Абстрактный класс Животных, НАСЛЕДУЕТ абстрактный класс Unit
public static abstract class Animal extends Unit {
    // тут могут быть Howие-то данные и/or методы
}
E já seus herdeiros Cordeiro ( Ovelha ):
// Баран НАСЛЕДУЕТ класс 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 +
                           " отсюда на четырёх копытах");
    }
}
e Preguiça ( Preguiça ):
// Ленивец НАСЛЕДУЕТ класс 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) + " вперед");
    }
}
O método move(int x, int y) da classe abstrata Unit não é abstract , então não precisamos substituí-lo, mas retardamos um pouco as coisas para o Sloth. Agora vamos passar para a ação.
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() + " ) умер");
}
Os literais numéricos 12, 50, 400 e 1000 são tomados de improviso; outros podem ser especificados, mas espero que a lógica esteja clara. Então, como podemos ver, tendo uma classe pai abstrata com métodos gerais, não precisamos pensar sobre qual classe específica é esta ou aquela unidade, mas simplesmente chamar esses métodos ( makeSound() e move ( ) ) . Após a primeira passagem no loop, quando o método move() for chamado em todas as unidades , o seguinte será exibido na tela: Interfaces para quem tem “muito interesse, mas não entende nada” - 1 Obviamente, todos os objetos, exceto a preguiça, exibem uma mensagem padrão do método move() do pai abstrato classe Unit , e a preguiça tem um movimento (método) foi redefinido . No entanto, uma classe abstrata não nos ajudará a descobrir o que uma determinada unidade “pode fazer”. É aqui que as interfaces entram em ação . Usando instanceof , descobrimos se esta unidade pode executar determinadas ações ( se ela suporta a interface que precisamos ) e, em caso afirmativo, usamos a conversão de tipo que já nos é familiar, por exemplo, usando ((CanFly) unit). fly() convertemos nosso objeto do tipo Unit para o tipo de interface CanFly e chamamos seu método fly() . E não importa a classe do nosso objeto, a única coisa importante é que ele indicou a habilidade de voar em seu “currículo”. Dizemos a ele: "Você sabe como, então voe! Não nos importa como você faz isso . " Ou seja, para nós, como desenvolvedores, isso significa que as classes que implementam a interface CanFly podem alterar a implementação do método fly() a qualquer momento e de qualquer forma , novas classes que o implementam podem aparecer, ou, inversamente, os desenvolvedores podem remova algumas das classes antigas. Mas contanto que esse método execute suas funções declaradas e o objeto voe, não nos importamos. Nosso código que funciona com objetos que implementam esta interface permanecerá o mesmo; não precisaremos mudar nada. Após o segundo ciclo, quando todos estiverem tentando escapar, dependendo da escala da enchente, veremos na tela ou Interfaces para quem tem “muito interesse, mas não entende nada” - 2 ou Interfaces para quem tem “muito interesse, mas não entende nada” - 3 Sem interface, teríamos que verificar cada objeto quanto à conformidade com uma determinada classe (e teríamos para verificar tudo) e tenha em mente as habilidades de cada classe específica. Ou seja, agora está um cordeiro na nossa frente e parece saber correr. E se você tiver várias dezenas ou centenas de tipos (classes) diferentes de tais personagens? E se não foi você quem os escreveu, mas outro programador, então você não tem ideia de quem pode fazer o quê? Seria muito mais difícil... E um pequeno acréscimo após a publicação: Na vida real, você não é o único trabalhando em um projeto. Digamos que você só faça lógica. Todos os objetos com os quais você interage são escritos por outros programadores. Você pode nem conhecer todas as classes com as quais seu código funciona. Tudo o que você precisa deles é que façam o que você precisa que eles façam. No entanto, todos eles podem fazer isso de maneiras completamente diferentes. Mas, digamos, no seu código você cria um método que funciona apenas com objetos de classes que suportam uma determinada interface
void doSomething(CanFly f)
ou seja, você define a interface usando o parâmetro method. Não é uma classe concreta, mas uma interface. E todos os outros programadores, ao chamar esse seu método void doSomething(CanFly), devem passar como argumentos um objeto explicitamente de uma classe que implementa CanFly, ou algum objeto de alguma classe que possa ser convertido para ele:
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
É assim que as interfaces podem ser úteis. E essas não são todas as suas capacidades e métodos de aplicação. Provavelmente aprenderemos mais posteriormente no curso =) Obrigado por ler até o fim =)
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION