resueltos - Métodos opcionales en la interfaz de Java
interfaces en java pdf (11)
Aunque no responde la pregunta del OP, vale la pena señalar que a partir de Java 8 agregar métodos por defecto a las interfaces es factible . La palabra clave default
colocada en la firma del método de una interfaz dará como resultado que una clase tenga la opción de anular el método, pero no lo requiera.
Según entiendo, si implementa una interfaz en Java, los métodos especificados en esa interfaz deben ser utilizados por las subclases que implementan dicha interfaz.
Me di cuenta de que en algunas interfaces, como la interfaz de Colección, hay métodos que se comentan como opcionales, pero ¿qué significa exactamente? Me impresionó un poco porque pensé que se requerirían todos los métodos especificados en la interfaz.
Bueno, este tema se ha dirigido a ... sí ... pero piensa, falta una respuesta. Estoy hablando de los "Métodos por defecto" de interfaces. Por ejemplo, imaginemos que tendrás una clase para cerrar cualquier cosa (como un destructor o algo así). Digamos que debería tener 3 métodos. Vamos a llamarlos "doFirst ()", "doLast ()" y "onClose ()".
Por lo tanto, decimos que queremos que cualquier objeto de ese tipo al menos realice "onClose ()", pero el otro es opcional.
Puede darse cuenta de eso, usando los "Métodos por defecto" de interfaces. Lo sé, esto haría que la mayoría de las veces niegue el motivo de una interfaz, pero si está diseñando un marco, esto puede ser útil.
Entonces, si quieres darte cuenta de esta manera, se vería el siguiente
public interface Closer {
default void doFirst() {
System.out.print("first ... ");
}
void onClose();
default void doLast() {
System.out.println("and finally!");
}
}
Lo que sucedería ahora, si por ejemplo lo implementaras en una clase llamada "Test", el compilador estaría perfectamente bien con lo siguiente:
public class TestCloser implements Closer {
@Override
public void onClose() {
System.out.print("closing ... ");
}
}
con la salida:
first ... closing ... and finally!
o
public class TestCloser implements Closer {
@Override
public void onClose() {
System.out.print("closing ... ");
}
@Override
public void doLast() {
System.out.println("done!");
}
}
con la salida:
first ... closing ... done!
Todas las combinaciones son posibles Cualquier cosa con "predeterminado" puede implementarse, pero no debe, sin embargo, cualquier cosa sin debe implementarse.
Espero que no esté del todo mal que ahora responda.
Que tengas un buen día a todos!
[edit1]: tenga en cuenta: esto solo funciona en Java 8.
De hecho, estoy inspirado por SurfaceView.Callback2. Creo que esta es la forma oficial
public class Foo {
public interface Callback {
public void requiredMethod1();
public void requiredMethod2();
}
public interface CallbackExtended extends Callback {
public void optionalMethod1();
public void optionalMethod2();
}
private Callback mCallback;
}
Si su clase no necesita implementar métodos opcionales, solo "implementa la Devolución de llamada". Si su clase necesita implementar métodos opcionales, simplemente "implementa CallbackExtended".
Lo siento por mierda Inglés.
En Java 8 y versiones posteriores, la respuesta a esta pregunta sigue siendo válida, pero ahora tiene más matices.
En primer lugar, estas declaraciones de la respuesta aceptada siguen siendo correctas:
- Las interfaces están destinadas a especificar sus comportamientos implícitos en un contrato (una declaración de reglas de comportamiento que las clases que implementan deben obedecer para que se considere válida)
- hay una distinción entre el contrato (reglas) y la implementación (codificación programática de las reglas)
- los métodos especificados en la interfaz SIEMPRE DEBEN implementarse (en algún punto)
Entonces, ¿cuál es el matiz que es nuevo en Java 8? Cuando se habla de "Métodos opcionales" cualquiera de los siguientes ahora es apto:
1. Un método cuya implementación es contractualmente opcional
La "tercera declaración" dice que los métodos de interfaz abstractos siempre deben implementarse y esto sigue siendo cierto en Java 8+. Sin embargo, como en Java Collections Framework, es posible describir algunos métodos de interfaz abstractos como "opcionales" en el contrato.
En este caso, el autor que está implementando la interfaz puede elegir no implementar el método. Sin embargo, el compilador insistirá en una implementación, por lo que el autor utiliza este código para cualquier método opcional que no se necesite en la clase de implementación particular:
public SomeReturnType optionalInterfaceMethodA(...) {
throw new UnsupportedOperationException();
}
En Java 7 y versiones anteriores, este era realmente el único tipo de "método opcional" que existía, es decir, un método que, si no se implementaba, arrojaba una UnsupportedOperationException. Este comportamiento se especifica necesariamente mediante el contrato de interfaz (por ejemplo, los métodos de interfaz opcional del Java Collections Framework).
2. Un método predeterminado cuya reimplantación es opcional
Java 8 introdujo el concepto de métodos predeterminados . Estos son métodos cuya implementación puede ser y es proporcionada por la propia definición de la interfaz. Generalmente, solo es posible proporcionar métodos predeterminados cuando el cuerpo del método se puede escribir utilizando otros métodos de interfaz (es decir, los "primitivos"), y cuando this
puede significar "este objeto cuya clase ha implementado esta interfaz".
Un método predeterminado debe cumplir el contrato de la interfaz (al igual que cualquier otra implementación de método de interfaz). Por lo tanto, la especificación de una implementación del método de interfaz en una clase implementadora queda a criterio del autor (siempre que el comportamiento sea adecuado para su propósito).
En este nuevo entorno, Java Collections Framework podría reescribirse como:
public interface List<E> {
:
:
default public boolean add(E element) {
throw new UnsupportedOperationException();
}
:
:
}
De esta forma, el método add()
"opcional" tiene el comportamiento predeterminado de arrojar una UnsupportedOperationException si la clase implementadora no proporciona un nuevo comportamiento propio, que es exactamente lo que le gustaría que ocurriera y que cumpla con el contrato de Lista. Si un autor está escribiendo una clase que no permite que se agreguen nuevos elementos a una implementación de la Lista, la implementación de add()
es opcional porque el comportamiento predeterminado es exactamente lo que se necesita.
En este caso, la "tercera declaración" anterior sigue siendo verdadera, porque el método se ha implementado en la interfaz misma.
3. Un método que devuelve un resultado Optional
El nuevo tipo final de método opcional es simplemente un método que devuelve un Optional
. La clase Optional
proporciona una forma decididamente más orientada a objetos de tratar con resultados null
.
En un estilo fluido de programación, como el tipo comúnmente visto al codificar con la nueva API de Java Streams, un resultado nulo en cualquier punto hace que el programa se bloquee con una NullPointerException. La clase Optional
proporciona un mecanismo para devolver resultados nulos al código del cliente de una manera que habilita el estilo fluido sin causar la falla del código del cliente.
Estaba buscando una forma de implementar la interfaz de devolución de llamada, por lo que era necesario implementar métodos opcionales ya que no quería implementar todos los métodos para cada devolución de llamada.
Entonces, en lugar de usar una interfaz, utilicé una clase con implementación vacía como:
public class MyCallBack{
public void didResponseCameBack(String response){}
}
Y puede establecer la variable miembro CallBack de esta manera,
c.setCallBack(new MyCallBack() {
public void didResponseCameBack(String response) {
//your implementation here
}
});
luego llámalo así.
if(mMyCallBack != null) {
mMyCallBack.didResponseCameBack(response);
}
De esta forma, no tendría que preocuparse por implementar todos los métodos por cada llamada devuelta, sino solo anular los que necesita.
Los métodos opcionales en la interfaz de Colección significan que la implementación del método puede lanzar una excepción, pero debe implementarse de todos modos. Como se especifica en los documentos :
Algunas implementaciones de colecciones tienen restricciones sobre los elementos que pueden contener. Por ejemplo, algunas implementaciones prohíben elementos nulos, y algunas tienen restricciones sobre los tipos de sus elementos. Intentar agregar un elemento inelegible arroja una excepción no verificada, normalmente NullPointerException o ClassCastException. Intentar consultar la presencia de un elemento inelegible puede arrojar una excepción, o simplemente puede devolver falso; algunas implementaciones exhibirán el comportamiento anterior y algunas exhibirán el último. De manera más general, intentar una operación en un elemento inelegible cuya finalización no resultaría en la inserción de un elemento inelegible en la colección puede arrojar una excepción o puede tener éxito, a elección de la implementación. Dichas excepciones se marcan como "opcionales" en la especificación para esta interfaz.
Para compilar una clase de implementación (no abstracta) para una interfaz, se deben implementar todos los métodos.
Sin embargo , si pensamos en un método que su implementación es una simple excepción lanzada como ''no implementado'' (como algunos métodos en la interfaz de Collection
), entonces la interfaz de Collection
es la excepción en este caso, no el caso regular. Por lo general , la implementación de clase debe (y lo hará) implementar todos los métodos.
El "opcional" en la colección significa que la clase de implementación no tiene que ''implementar'' (de acuerdo con la terminología anterior) y lanzará NotSupportedException
).
Un buen ejemplo de método add()
para colecciones inmutables: el concreto implementará un método que no hace más que arrojar NotSupportedException
En el caso de Collection
, se hace para evitar árboles de herencia desordenados, lo que hará que los programadores sean miserables, pero en la mayoría de los casos, este paradigma no se aconseja, y debe evitarse si es posible.
Actualizar:
A partir de java 8, se introdujo un método predeterminado .
Eso significa que una interfaz puede definir un método, incluida su implementación.
Esto se agregó para permitir la adición de funcionalidad a las interfaces, a la vez que admite la compatibilidad con versiones anteriores para las partes de código que no necesitan la nueva funcionalidad.
Tenga en cuenta que el método todavía está implementado por todas las clases que lo declaran, pero utilizando la definición de la interfaz.
Parece que hay una gran confusión en las respuestas aquí.
El lenguaje Java requiere que cada método en una interfaz sea implementado por cada implementación de esa interfaz. Período. No hay excepciones para esta regla. Decir "Las colecciones son una excepción" sugiere una comprensión muy confusa de lo que realmente está sucediendo aquí.
Es importante darse cuenta de que hay una especie de dos niveles de conformidad con una interfaz:
Lo que el lenguaje Java puede verificar. Esto se reduce a: ¿hay alguna implementación para cada uno de los métodos?
En realidad cumpliendo el contrato. Es decir, ¿la implementación hace lo que la documentación en la interfaz dice que debería?
Las interfaces bien escritas incluirán documentación que explica exactamente qué se espera de las implementaciones. Tu compilador no puede verificar esto por ti. Necesita leer los documentos y hacer lo que dicen. Si no hace lo que dice el contrato, entonces tendrá una implementación de la interfaz en lo que respecta al compilador , pero será una implementación defectuosa / no válida.
Al diseñar la API de colecciones, Joshua Bloch decidió que, en lugar de tener interfaces muy precisas para distinguir entre las diferentes variantes de colecciones (por ejemplo: legible, grabable, de acceso aleatorio, etc.), solo tendría un conjunto de interfaces muy grueso, principalmente Collection
, List
, Set
y Map
, y luego documente ciertas operaciones como "opcionales". Esto fue para evitar la explosión combinatoria que resultaría de las interfaces de grano fino. De las Preguntas frecuentes sobre diseño de la API de Java Collections :
Para ilustrar el problema en detalle sangriento, suponga que desea agregar la noción de modificabilidad a la Jerarquía. Necesita cuatro nuevas interfaces: ModifiableCollection, ModifiableSet, ModifiableList y ModifiableMap. Lo que antes era una jerarquía simple ahora es una jerarquía desordenada. Además, necesita una nueva interfaz Iterator para usar con Colecciones no modificables, que no contiene la operación de eliminación. ¿Ahora puede deshacerse de UnsupportedOperationException? Lamentablemente no.
Considere las matrices. Implementan la mayoría de las operaciones de la Lista, pero no las eliminan ni las agregan. Son listas de "tamaño fijo". Si desea capturar esta noción en la jerarquía, debe agregar dos nuevas interfaces: VariableSizeList y VariableSizeMap. No tiene que agregar VariableSizeCollection y VariableSizeSet, porque serían idénticos a ModifiableCollection y ModifiableSet, pero puede optar por agregarlos de todos modos por coherencia. Además, necesita una nueva variedad de ListIterator que no admita las operaciones de agregar y eliminar, para ir junto con la Lista no modificable. Ahora tenemos hasta diez o doce interfaces, más dos nuevas interfaces Iterator, en lugar de las cuatro originales. ¿Terminamos? No.
Considere registros (como registros de errores, registros de auditoría y diarios para objetos de datos recuperables). Son secuencias naturales de solo anexión, que admiten todas las operaciones de la lista, excepto para eliminar y establecer (reemplazar). Requieren una nueva interfaz de núcleo y un nuevo iterador.
¿Y qué hay de las colecciones inmutables, a diferencia de las inmodificables? (es decir, Colecciones que el cliente no puede cambiar Y nunca cambiará por ningún otro motivo). Muchos argumentan que esta es la distinción más importante de todas, porque permite que múltiples hilos accedan a una colección al mismo tiempo sin la necesidad de sincronización. Agregar este soporte a la jerarquía de tipos requiere cuatro interfaces más.
Ahora tenemos más de una veintena de interfaces y cinco iteradores, y es casi seguro que todavía hay colecciones que surgen en la práctica y que no encajan limpiamente en ninguna de las interfaces. Por ejemplo, las vistas de colección devueltas por Map son colecciones naturales de solo eliminación. Además, hay colecciones que rechazarán ciertos elementos en función de su valor, por lo que todavía no hemos eliminado las excepciones de tiempo de ejecución.
Cuando todo estuvo dicho y hecho, sentimos que era un buen compromiso de ingeniería eludir todo el problema al proporcionar un conjunto muy pequeño de interfaces centrales que pueden arrojar una excepción de tiempo de ejecución.
Cuando los métodos en la API de colecciones se documentan como "operaciones opcionales", no significa que simplemente se puede dejar la implementación del método en la implementación, ni tampoco se puede usar un cuerpo de método vacío (por un lado, muchos de ellos necesitan devolver un resultado). Más bien, significa que una opción de implementación válida (una que todavía se ajusta al contrato) es lanzar una UnsupportedOperationException
.
Tenga en cuenta que, debido a que UnsupportedOperationException
es una RuntimeException
, puede lanzarlo desde cualquier implementación de método, en lo que respecta al compilador. Por ejemplo, puede lanzarlo desde una implementación de Collection.size()
. Sin embargo, tal implementación violaría el contrato ya que la documentación para Collection.size()
no dice que esto está permitido.
Aparte: el enfoque utilizado por la API de colecciones de Java es algo controvertido (probablemente menos ahora que cuando se introdujo por primera vez, sin embargo). En un mundo perfecto, las interfaces no tendrían operaciones opcionales, y en su lugar se usarían interfaces de grano fino. El problema es que Java no admite tipos estructurales inferidos o tipos de intersección, por lo que intentar hacer las cosas de la "manera correcta" termina volviéndose extremadamente difícil de manejar en el caso de las colecciones.
Si examinamos el código de AbstractCollection.java en grepCode, que es una clase antecesora para todas las implementaciones de colecciones, nos ayudará a comprender el significado de los métodos opcionales. Aquí está el código para el método add (e) en la clase AbstractCollection. El método add (e) es opcional de acuerdo con la interfaz de colección
public boolean add(E e) {
throw new UnsupportedOperationException();
}
Método opcional significa que ya está implementado en las clases antecesoras y arroja UnsupportedOperationException en el momento de la invocación. Si queremos que nuestra colección se pueda modificar, entonces debemos anular los métodos opcionales en la interfaz de colección.
Todos los métodos deben implementarse para que el código se compile (aparte de aquellos con implementaciones default
en Java 8+), pero la implementación no tiene que hacer nada funcionalmente útil. Específicamente, esto:
- Puede estar en blanco (un método vacío)
- Puede arrojar una
UnsupportedOperationException
(o similar)
Este último enfoque a menudo se toma en las clases de colección: todos los métodos todavía están implementados, pero algunos pueden arrojar una excepción si se los llama en el tiempo de ejecución.
Una interfaz en Java solo declara el contrato para implementar clases. Todos los métodos en esa interfaz deben implementarse, pero las clases de implementación son libres de dejarlos sin implementar, es decir, en blanco. Como un ejemplo artificial,
interface Foo {
void doSomething();
void doSomethingElse();
}
class MyClass implements Foo {
public void doSomething() {
/* All of my code goes here */
}
public void doSomethingElse() {
// I leave this unimplemented
}
}
Ahora he dejado doSomethingElse()
sin implementar, dejándolo libre para que implementen mis subclases. Eso es opcional
class SubClass extends MyClass {
@Override
public void doSomethingElse() {
// Here''s my implementation.
}
}
Sin embargo, si habla de interfaces de Colección, como han dicho otros, son una excepción. Si no se implementan ciertos métodos y los llama, pueden lanzar excepciones UnsupportedOperationException
.