Cada nova versão do Java difere das anteriores. Como exemplo dessa mudança do material que abordamos, a linguagem não existia enumsantes do Java 5.
Métodos padrão em interfaces - 1
Da mesma forma, o Java 8 é visivelmente diferente do Java 7. Não vamos ignorar inovações importantes, é claro. Já que estamos falando sobre interfaces nesta lição, vamos considerar uma atualização da linguagem: métodos padrão em interfaces . Você já sabe que uma interface não implementa comportamento . Sua finalidade é descrever qual comportamento deve existir em todos os objetos que implementam a interface . Mas os desenvolvedores frequentemente encontravam situações em que a implementação de um método era a mesma em todas as classes. Vejamos nosso exemplo de carro antigo:

public interface Car {

   public void gas();
  
   public void brake();
}
public class Sedan implements Car {

   @Override
   public void gas() {
       System.out.println("Gas!");
   }

   @Override
   public void brake() {
       System.out.println("Brake!");
   }
}


public class Truck implements Car {

   @Override
   public void go() {
       System.out.println("Gas!");
   }

   @Override
   public void brake() {
       System.out.println("Brake!");
   }
}


public class F1Car implements Car {
   @Override
   public void go() {
       System.out.println("Gas!");
   }

   @Override
   public void brake() {
       System.out.println("Brake!");
   }
}
Qual você acha que é o principal problema com esse código? Você provavelmente notou que escrevemos um monte de código duplicado! Este é um problema comum na programação e deve ser evitado. Outra questão é que não havia soluções específicas antes do lançamento do Java 8. Quando essa versão foi lançada, tornou-se possível definir métodos padrão e implementá-los diretamente dentro de uma interface! Veja como fazer:

public interface Car {

   public default void gas() {
       System.out.println("Gas!");
   }

   public default void brake() {
       System.out.println("Brake!");
   }
}

public class Sedan implements Car {

}

public class Truck implements Car {

}

public class F1Car implements Car {

}
Agora os métodos gas()e brake(), que eram os mesmos para todos os carros, foram movidos para a interface, eliminando a necessidade de código duplicado. E os métodos estão disponíveis em cada uma das aulas!

public class Main {

   public static void main(String[] args) {

       F1Car f1Car = new F1Car();
       Sedan sedan = new Sedan();
       Truck truck = new Truck();
       truck.gas();
       sedan.gas();
       f1Car.brake();
   }
}
E se houver 100 classes com um gas()método, mas apenas 99 delas tiverem o mesmo comportamento? Isso estraga tudo? Um método padrão não funcionará neste caso? Claro que não :) Os métodos de interface padrão podem ser substituídos.

public class UnusualCar implements Car {
   @Override
   public void go() {
       System.out.println("This car accelerates differently!");
   }

   @Override
   public void brake() {
       System.out.println("This car slows down differently!");
   }
}
Todos os outros 99 tipos de carros empregarão o método padrão, enquanto oUnusualCarclasse é uma exceção. Sem estragar o quadro geral, ele definirá com calma seu próprio comportamento. Herança múltipla em interfaces. Como você já sabe, não existe herança múltipla em Java. Há muitas razões para isto. Vamos considerá-los em detalhes em uma lição separada. Em outras linguagens, como C++, a situação é inversa. Nenhuma herança múltipla apresenta um grande desafio, pois um mesmo objeto pode ter várias características e comportamentos diferentes. Por exemplo, somos filhos para nossos pais, alunos para nossos professores e pacientes para nossos médicos. Na vida real, desempenhamos vários papéis e, portanto, nos comportamos de maneira diferente: obviamente, interagimos com os professores de maneira diferente do que com amigos próximos. Vamos tentar traduzir essa situação em código. Vamos imaginar que temos duas classes: Pond e Aviary. Um lago precisa de pássaros nadadores, enquanto um aviário precisa de pássaros voadores. Para representar isso, criamos duas classes base:FlyingBirde Waterfowl.

public class Waterfowl {
}

public class FlyingBird {
}
Assim, enviaremos as aves que herdam FlyingBirdpara o aviário, enquanto as que derivam Waterfowlirão para a lagoa. Tudo parece simples. Mas o que devemos fazer se precisarmos definir um pato em algum lugar? Os patos nadam e voam. Mas não temos herança múltipla. Felizmente, o Java oferece suporte a várias implementações de interfaces. Se uma classe não pode herdar vários pais, é fácil implementar várias interfaces! Nosso pato pode ser tanto um pássaro voador quanto um pássaro nadador :) Para atingir o resultado desejado, tudo o que precisamos fazer é criar FlyingBirdinterfaces Waterfowlem vez de classes.

public class Duck implements FlyingBird, Waterfowl {

   // Methods of both interfaces combine easily into one class
  
   @Override
   public void fly() {
       System.out.println("Flying!");
   }

   @Override
   public void swim() {

       System.out.println("Swimming!");
   }
}
Isso significa que nosso programa mantém a capacidade de gerenciar classes de forma flexível. Quando combinamos isso com métodos padrão, nossa capacidade de determinar o comportamento dos objetos torna-se quase ilimitada! :)