Métodos de Interfaces en Java

Intermedio

Como se explicó anteriormente, antes de JDK 8, una interfaz no podía definir ninguna implementación de ningún tipo. Esto significaba que para todas las versiones anteriores de Java, los métodos especificados por una interfaz eran abstractos y no contenían ningún cuerpo.

El lanzamiento de JDK 8 cambió esto al agregar una nueva capacidad a interface denominada método predeterminado. Un método predeterminado le permite definir una implementación predeterminada para un método de interfaz. En otras palabras, mediante el uso de un método predeterminado, es posible que un método de interfaz proporcione un cuerpo, en lugar de ser abstracto. Durante su desarrollo, el método predeterminado también se denominó método de extensión, y es probable que vea ambos términos utilizados.

Una motivación principal para el método predeterminado era proporcionar un medio por el cual las interfaces pudieran expandirse sin romper el código existente. Recuerde que debe haber implementaciones para todos los métodos definidos por una interfaz. En el pasado, si se agregaba un nuevo método a una interfaz popular y ampliamente utilizada, la adición de ese método rompería el código existente porque no se encontraría ninguna implementación para ese método.

1. El método predeterminado (default)

El método predeterminado resuelve este problema suministrando una implementación que se usará si no se proporciona explícitamente otra implementación. Por lo tanto, la adición de un método predeterminado no causará la ruptura del código preexistente.

Otra motivación para el método predeterminado fue el deseo de especificar métodos en una interfaz que son, esencialmente, opcionales, dependiendo de cómo se usa la interfaz. Por ejemplo, una interfaz puede definir un grupo de métodos que actúan sobre una secuencia de elementos.

  • Uno de estos métodos podría llamarse remover(), y su propósito es eliminar un elemento de la secuencia. Sin embargo, si la interfaz está diseñada para soportar secuencias modificables y no modificables, entonces remover() es esencialmente opcional porque no será utilizado por secuencias no modificables.

En el pasado, una clase que implementaba una secuencia no modificable debería haber definido una implementación vacía de remover(), aunque no era necesaria. Hoy en día, se puede especificar una implementación predeterminada para remover() en la interfaz que no hace nada o informa un error. Proporcionar este valor predeterminado evita que una clase utilizada para secuencias no modificables tenga que definir su propia versión de marcador de posición de remover(). Por lo tanto, al proporcionar un valor predeterminado, la interfaz realiza la implementación de remover() por una clase opcional.

Es importante señalar que la adición de métodos predeterminados no cambia un aspecto clave de la interfaz: una interfaz todavía no puede tener variables de instancia. Por lo tanto, la diferencia de definición entre una interfaz y una clase es que una clase puede mantener información de estado, pero una interfaz no puede. Además, aún no es posible crear una instancia de una interfaz por sí mismo. Debe ser implementado por una clase. Por lo tanto, a pesar de que, comenzando con JDK 8, una interfaz puede definir métodos predeterminados, la interfaz debe ser implementada por una clase si se va a crear una instancia.

Un último punto: como regla general, los métodos predeterminados constituyen una característica de propósito especial. Las interfaces que cree se usarán principalmente para especificar el qué y no el cómo. Sin embargo, la inclusión del método predeterminado le brinda mayor flexibilidad.

2. Fundamentos del método default

Un método predeterminado de la interfaz se define de forma similar a la forma en que una clase define un método. La principal diferencia es que la declaración está precedida por la palabra clave default. Por ejemplo, considere esta interfaz simple:

public interface MiInterfaz {
    //Esto es una declaración normal de un método
    //Esto no define la implementación de default
    int getUsuario();

    //Esto es un método default
    default int getAdmin(){
        return 1;
    }
}

MiInterfaz declara dos métodos. El primero, getUsuario(), es una declaración de método de interfaz estándar. No define ninguna implementación en absoluto. El segundo método es getAdmin(), e incluye una implementación predeterminada (default). En este caso, simplemente devuelve 1.

Preste especial atención a la forma en que se declara getAdmin(). Su declaración está precedida por el modificador default. Esta sintaxis puede ser generalizada. Para definir un método predeterminado, preceda su declaración con default.

Como getAdmin() incluye una implementación predeterminada, no es necesario que una clase implementadora lo sobrescriba. En otras palabras, si una clase implementadora no proporciona su propia implementación, se utiliza el valor predeterminado. Por ejemplo, la clase MiIntImp que se muestra a continuación es perfectamente válida:

class MiIntImp implements MiInterfaz {
    //Solo getUsuario() definida en MiInterfaz
    //debe implementarse
    public int getUsuario(){
        return 100;
    }
}

El siguiente código crea una instancia de MiIntImp y la usa para llamar tanto a getUsuario() como a getAdmin().

//Uso del Metodo Default
public class MetDefaultDemo {
    public static void main(String[] args) {
        MiIntImp ob=new MiIntImp();

        //Puede llamar a getUsuario(), porque
        //está explícitamente implementado por MiIntImp
        System.out.println("Usuario: "+ob.getUsuario());

        //También puede llamar a getAdmin(), porque
        //está implementado por defecto
        System.out.println("Admin: "+ob.getAdmin());
    }
}

Salida:

Usuario: 100
Admin: 1

Como puede ver, la implementación predeterminada de getAdmin() se usó automáticamente. No fue necesario que MiIntImp lo definiera. Por lo tanto, para getAdmin(), la implementación por una clase es opcional. (Por supuesto, su implementación por parte de una clase será necesaria si la clase necesita devolver algo diferente.)

Es posible y común que una clase implementadora defina su propia implementación de un método predeterminado. Por ejemplo, MiIntImp2 anula getAdmin(), como se muestra aquí:

class MiIntImp2 implements MiInterfaz{

    //Aquí se proporcionan los métodos para getUsuario() y getAdmin()
    public int getUsuario() {
        return 100;
    }

    public int getAdmin() {
        return 42;
    }
}

Ahora, cuando se llama a getAdmin(), se devuelve un valor diferente al predeterminado.

3. Métodos static en una interfaz

JDK 8 agregó otra nueva capacidad a la interfaz: la capacidad de definir uno o más métodos estáticos. Al igual que los métodos estáticos en una clase, un método estático definido por una interfaz se puede llamar independientemente de cualquier objeto. Por lo tanto, no es necesaria la implementación de la interfaz, y no se requiere ninguna instancia de la interfaz para llamar a un método estático. En su lugar, se llama a un método estático especificando el nombre de la interfaz, seguido de un punto, seguido del nombre del método. Aquí está la forma general:

NombreInterface.nombreMetodoStatic

Tenga en cuenta que esto es similar a la forma en que se llama un método estático en una clase. A continuación, se muestra un ejemplo de un método estático en una interfaz al agregar 1 método a MiInterfaz, que se muestra anteriormente. El método estático es getUniversal(). Y devuelve cero.

public interface MiInterfaz {
    //Esto es una declaración normal de un método
    //Esto no define la implementación de default
    int getUsuario();

    //Esto es un método default
    default int getAdmin(){
        return 1;
    }

    //Esto es un método static de interfaz
    static int getUniversal(){
        return 0;
    }
}

Se puede llamar al método getUniversal(), como se muestra aquí:

int univ = MiInterfaz.getUniversal();

Como se mencionó, no se requiere implementación o instancia de MiInterfaz para llamar a getUniversal() porque es estático (static).

Un último punto: los métodos de interfaz estática no son heredados por una clase implementadora o una subinterfaz.

4. Métodos private de interfaz

A partir de JDK 9, una interfaz puede incluir un método privado (private). Se puede llamar a un método privado de interfaz solo por un método predeterminado u otro método privado definido por la misma interfaz. Debido a que un método privado de interfaz se especifica como private, el código no puede usarlo fuera de la interfaz en la que está definido. Esta restricción incluye subinterfaces porque un método de interfaz privado no es heredado por una subinterfaz.

El beneficio clave de un método privado de interfaz es que permite que dos o más métodos predeterminados utilicen una pieza común de código, evitando la duplicación de código.

Por ejemplo, aquí hay una versión mejorada de la interfaz de Series (que tomamos como ejemplo para Introducción a Interfaces) que agrega un segundo método predeterminado llamado saltarGetSiguienteArray(). Se salta una cantidad específica de elementos y luego devuelve una matriz que contiene los elementos subsiguientes. Utiliza un método privado llamado getArray() para obtener una matriz de elementos de un tamaño específico.

//Versión mejorada de Series que incluye dos métodos default
//que usa un método privado llamado getArray()
public interface Series {
    int getSiguiente(); //Retorna el siguiente número de la serie

    //Retorna un array que contiene el siguiente n elemento
    //más allá del elemento actual
    default int[] getSiguienteArray(int n){
        return getArray(n);
    }

    //Retorna un array que contiene el siguiente n elemento
    //después de saltar elementos
    default int[] saltarGetSiguienteArray(int saltar,int n){

        //Se especifica el número de saltos
        getArray(saltar);
            return getArray(n);
    }

    //Un método privado que retorna un array que contiene
    //los siguientes n elementos
    private int[] getArray(int n){
        int[] valores=new int[n];
        for (int i=0; i<n;i++) valores[i]=getSiguiente();
        return valores;
    }

    void reiniciar();
    void setEmpezar(int x);
    
}

Tenga en cuenta que tanto getSiguienteArray() como saltarGetSiguienteArray() usan el método privado getArray() para obtener la matriz para devolver. Esto evita que ambos métodos tengan que duplicar la misma secuencia de códigos. Tenga en cuenta que debido a que getArray() es privado, no puede ser llamado por código fuera de Series. Por lo tanto, su uso está limitado a los métodos predeterminados dentro de Series.

Aunque el método privado de interfaz es una función que rara vez necesitará, en los casos en que la necesite, la encontrará bastante útil.

5. Conclusiones sobre paquetes e interfaces

Aunque los ejemplos que hemos incluido en este blog no hacen uso frecuente de paquetes o interfaces, estas dos herramientas son una parte importante del entorno de programación de Java. Prácticamente todos los programas reales que escriba en Java estarán contenidos dentro de los paquetes. Un número probablemente implementará interfaces también.

Como verá más adelante, los paquetes juegan un papel importante en la nueva función de módulo agregada por JDK 9. Es importante, por lo tanto, que se sienta cómodo con su uso.

Interfaces Java
  • Métodos de Interfaces

Sobre el Autor:

Hey hola! Yo soy Alex Walton y tengo el placer de compartir conocimientos hacía ti sobre el tema de Programación en Java, desde cero, Online y Gratis.

Deja una Respuesta

*

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.