1. Introducción a las interfaces

Hoy es tu día para el conocimiento. Otro tema nuevo e interesante son las interfaces.

El concepto de interfaz es hijo de los principios de abstracción y polimorfismo. Una interfaz es muy similar a una clase abstracta, en la que todos los métodos son abstractos. Se declara de la misma forma que una clase, pero usamos la interfacepalabra clave.

interface Feline
{
   void purr();
   void meow();
   void growl();
}

Aquí hay algunos datos útiles sobre las interfaces:

1. Declarar una interfaz

interface Drawable
{
   void draw();
}

interface HasValue
{
   int getValue();
}
  1. En lugar de la classpalabra clave, escribimos interface.
  2. Contiene solo métodos abstractos (no escriba la abstractpalabra clave)
  3. De hecho, las interfaces tienen todospublic los métodos
2. Herencia de interfaz

Una interfaz solo puede heredar interfaces. Pero una interfaz puede tener muchos padres. Otra forma de decir esto es decir que Java tiene herencia múltiple de interfaces. Ejemplos:

interface Piece extends Drawable, HasValue
{
   int getX();
   int getY();
}

3. Heredar clases de interfaces

Una clase puede heredar múltiples interfaces (solo de una clase). Esto se hace usando la implementspalabra clave. Ejemplo:

abstract class ChessItem implements Drawable, HasValue
{
   private int x, y, value;
   public int getValue()
   {
      return value;
   }

   public int getX()
   {
      return x;
   }

   public  int getY()
   {
      return y;
   }
}

La clase ChessItem se declara abstracta: implementa todos los métodos heredados excepto draw. En otras palabras, la ChessItemclase contiene un método abstracto: draw().

El significado técnico de las palabras clave extendsy implementses el mismo: ambas son herencia. La distinción se hizo para mejorar la legibilidad del código. También decimos que las clases se heredan (vía extends) y las interfaces se implementan (vía implements)

4. Variables

Aquí está lo más importante: las variables ordinarias no se pueden declarar en las interfaces (aunque las estáticas sí).

Pero, ¿por qué necesitamos interfaces? ¿Cuándo se usan? Las interfaces tienen dos fuertes ventajas sobre las clases:



2. Separar la "descripción de métodos" de su implementación.

Anteriormente, dijimos que si desea permitir que los métodos de su clase sean llamados desde otras clases, entonces sus métodos deben estar marcados con la publicpalabra clave. Si desea que algunos de esos métodos se llamen solo desde dentro de su clase, debe marcarlos con la privatepalabra clave. En otras palabras, dividimos los métodos de la clase en dos categorías: "para que todos los usen" y "solo para nuestro propio uso".

Las interfaces ayudan a fortalecer aún más esta división. Haremos una "clase especial para que todos la usen", así como una segunda clase "solo para nuestro propio uso", que heredará la primera clase. Así es más o menos cómo se vería:

Antes Después
class Student
{
   private String name;
   public Student(String name)
   {
      this.name = name;
   }

   public String getName()
   {
      return this.name;
   }

   private void setName(String name)
   {
      this.name = name;
   }
interface Student
{
   public String getName();
}

class StudentImpl implements Student
{
   private String name;
   public StudentImpl(String name)
   {
      this.name = name;
   }

   public String getName()
   {
      return this.name;
   }

   private void setName(String name)
   {
      this.name = name;
   }
}
public static void main(String[] args)
{
   Student student = new Student("Alibaba");
   System.out.println(student.getName());
}
public static void main(String[] args)
{
   Student student = new StudentImpl("Ali")
   System.out.println(student.getName());
}

Dividimos nuestra clase en dos: una interfaz y una clase que hereda la interfaz . ¿Y cuál es la ventaja aquí?

Muchas clases diferentes pueden implementar (heredar) la misma interfaz. Y cada uno puede tener su propio comportamiento. Por ejemplo, ArrayList LinkedListson dos implementaciones diferentes de la Listinterfaz.

Por lo tanto, ocultamos no solo las diversas implementaciones, sino también la propia clase de implementación (ya que solo necesitamos la interfaz en el código). Esto nos permite ser muy flexibles: justo cuando el programa se ejecuta, podemos reemplazar un objeto con otro, cambiando el comportamiento de un objeto sin afectar a todas las clases que lo usan.

Esta es una técnica muy poderosa cuando se combina con polimorfismo. Por ahora, está lejos de ser obvio por qué deberías hacer esto. Primero debe encontrar programas con docenas o cientos de clases para comprender que las interfaces pueden hacer su vida mucho más fácil que sin ellas.


3. Herencia múltiple

En Java, todas las clases solo pueden tener una clase principal. En otros lenguajes de programación, las clases a menudo pueden tener varias clases principales. Esto es muy conveniente, pero también trae muchos problemas.

Los creadores de Java llegaron a un compromiso: prohibieron la herencia múltiple de clases, pero permitieron la herencia múltiple de interfaces. Una interfaz puede tener varias interfaces principales. Una clase puede tener varias interfaces principales, pero solo una clase principal.

¿Por qué prohibieron la herencia múltiple de clases pero permitieron la herencia múltiple de interfaces? Debido al llamado problema de herencia de diamantes:

Herencia múltiple

Cuando la clase B hereda la clase A, no sabe nada de las clases C y D. Por lo tanto, utiliza las variables de la clase A como mejor le parezca. La clase C hace lo mismo: utiliza las variables de la clase A, pero de forma diferente. Y todo esto resulta en un conflicto en la clase D.

Veamos el siguiente ejemplo sencillo. Digamos que tenemos 3 clases:

class Data
{
   protected int value;
}
class XCoordinate extends Data
{
   public void setX (int x) { value = x;}
   public int getX () { return value;}
}
class YCoordinate extends Data
{
   public void setY (int y) { value = y;}
   public int getY () { return value; }
}

La clase de datos almacena la valuevariable. Su clase descendiente XCoordinate usa esa variable para almacenar el xvalor, y la YCoordinateclase descendiente la usa para almacenar el yvalor.

Y funciona. Por separado. Pero si queremos que la clase XYCoordinates herede las clases XCoordinatey YCoordinate, obtenemos un código roto. Esta clase tendrá los métodos de sus clases antepasadas, pero no funcionarán correctamente porque tienen el mismo value variable.

Pero debido a que las interfaces no pueden tener variables, no pueden tener este tipo de conflicto. En consecuencia, se permite la herencia múltiple de interfaces.