Cada nueva versión de Java difiere de las anteriores. Como ejemplo de tal cambio del material que hemos cubierto, el lenguaje no tenía enumsantes de Java 5.
Métodos predeterminados en las interfaces - 1
De la misma manera, Java 8 es notablemente diferente de Java 7. Por supuesto, no ignoraremos las innovaciones importantes. Ya que estamos hablando de interfaces en esta lección, consideremos una actualización del lenguaje: métodos predeterminados en interfaces . Ya sabes que una interfaz no implementa comportamiento . Su propósito es describir qué comportamiento debe existir en todos los objetos que implementan la interfaz . Pero los desarrolladores encontraron con frecuencia situaciones en las que la implementación de un método era la misma en todas las clases. Veamos nuestro ejemplo de coche viejo:

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!");
   }
}
¿Cuál crees que es el principal problema de este código? ¡Probablemente hayas notado que escribimos un montón de código duplicado! Este es un problema común en la programación y debe evitarse. Otra cuestión es que no hubo soluciones particulares antes del lanzamiento de Java 8. Cuando salió esa versión, ¡se hizo posible definir métodos predeterminados e implementarlos directamente dentro de una interfaz! Aquí está cómo hacerlo:

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 {

}
Ahora los métodos gas()y brake(), que eran los mismos para todos los automóviles, se han trasladado a la interfaz, lo que elimina la necesidad de duplicar el código. ¡Y los métodos están disponibles en cada una de las clases!

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();
   }
}
¿Qué pasa si hay 100 clases con un gas()método, pero solo 99 de ellas deberían tener el mismo comportamiento? ¿Eso estropea todo? ¿Un método predeterminado no funcionará en este caso? Por supuesto que no :) Los métodos de interfaz predeterminados se pueden anular.

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 los otros 99 tipos de automóviles emplearán el método predeterminado, mientras que elUnusualCarla clase es una excepción. Sin estropear el panorama general, definirá con calma su propio comportamiento. Herencia múltiple en interfaces. Como ya sabes, no hay herencia múltiple en Java. Hay muchas razones para esto. Los consideraremos en detalle en una lección separada. En otros lenguajes, como C++, la situación se invierte. Ninguna herencia múltiple presenta un desafío serio, ya que un mismo objeto puede tener varias características y comportamientos diferentes. Por ejemplo, somos niños para nuestros padres, estudiantes para nuestros maestros y pacientes para nuestros médicos. En la vida real, cumplimos varios roles y, en consecuencia, nos comportamos de manera diferente: obviamente, interactuamos con los maestros de manera diferente que con los amigos cercanos. Intentemos traducir esta situación en código. Imaginemos que tenemos dos clases: Pond y Aviary. Un estanque necesita pájaros nadadores, mientras que un aviario necesita pájaros voladores. Para representar esto, creamos dos clases base:FlyingBirdy Waterfowl.

public class Waterfowl {
}

public class FlyingBird {
}
En consecuencia, enviaremos las aves que heredan FlyingBirdal aviario, mientras que las que derivan Waterfowlirán al estanque. Todo parece sencillo. Pero, ¿qué debemos hacer si necesitamos definir un pato en alguna parte? Los patos nadan y vuelan. Pero no tenemos herencia múltiple. Afortunadamente, Java admite múltiples implementaciones de interfaces. Si una clase no puede heredar múltiples padres, ¡es fácil implementar múltiples interfaces! Nuestro pato puede ser tanto un pájaro volador como un pájaro nadador :) Para lograr el resultado deseado, todo lo que tenemos que hacer es crear FlyingBirdinterfaces Waterfowlen lugar de clases.

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!");
   }
}
Esto significa que nuestro programa conserva la capacidad de administrar las clases de manera flexible. Cuando combinamos eso con los métodos predeterminados, nuestra capacidad para determinar el comportamiento de los objetos se vuelve casi ilimitada. :)