Java8: ¿Por qué está prohibido definir un método predeterminado para un método de java?lang.Objeto
Los métodos predeterminados son una buena herramienta nueva en nuestra caja de herramientas de Java. Sin embargo, intenté escribir una interfaz que definiera una versión default
del método toString
. Java me dice que esto está prohibido, ya que los métodos declarados en java.lang.Object
pueden no ser default
ed. ¿Por qué es este el caso?
Sé que existe la regla "la clase base siempre gana", por lo que por defecto (juego de palabras ;), cualquier implementación default
de un método Object
serÃa sobrescrita por el método de Object
de todos modos. Sin embargo, no veo ninguna razón por la que no deberÃa ser una excepción para los métodos de Object
en la especificación. Especialmente para toString
podrÃa ser muy útil tener una implementación predeterminada.
Entonces, ¿cuál es la razón por la que los diseñadores de Java decidieron no permitir default
métodos sobreescribiendo métodos de Object
?
5 answers
Este es otro de esos problemas de diseño del lenguaje que parece "obviamente una buena idea" hasta que empiezas a cavar y te das cuenta de que en realidad es una mala idea.
Este mail tiene mucho sobre el tema (y sobre otros temas también. Hubo varias fuerzas de diseño que convergieron para llevarnos al diseño actual:
- El deseo de mantener el modelo de herencia simple; {[18]]}
- El hecho de que una vez que miras más allá de los ejemplos obvios (por ejemplo, girando
AbstractList
en una interfaz), se da cuenta de que heredar es igual a/hashCode/toString está fuertemente ligado a la herencia única y el estado, y las interfaces se multiplican heredadas y sin estado; - Que potencialmente abrió la puerta a algunos comportamientos sorprendentes.
Ya has tocado el objetivo de "keep it simple" ; las reglas de herencia y resolución de conflictos están diseñadas para ser muy simples (las clases ganan a las interfaces, las interfaces derivadas ganan a las superinterfaces y cualquier otra los conflictos son resueltos por la clase implementadora. Por supuesto, estas reglas podrÃan ser retocadas para hacer una excepción, pero creo que encontrarás cuando empieces a tirar de esa cadena, que la complejidad incremental no es tan pequeña como podrÃas pensar.
Por supuesto, hay cierto grado de beneficio que justificarÃa más complejidad, pero en este caso no está ahÃ. Los métodos de los que estamos hablando aquà son equals, hashCode y toString. Todos estos métodos son intrÃnsecamente acerca del objeto estado, y es la clase que posee el estado, no la interfaz, que está en la mejor posición para determinar lo que significa la igualdad para esa clase (especialmente porque el contrato para la igualdad es bastante fuerte; ver Java eficaz para algunas consecuencias sorprendentes); los escritores de interfaz están demasiado lejos.
Es fácil sacar la AbstractList
ejemplo, serÃa fantástico si pudiéramos deshacernos de AbstractList
y poner el comportamiento en el List
interfaz. Pero una vez que te muevas más allá de este ejemplo obvio, no hay muchos otros buenos ejemplos. En la raÃz, AbstractList
está diseñado para una sola herencia. Pero las interfaces deben estar diseñadas para herencia múltiple.
Además, imagina que estás escribiendo esta clase:
class Foo implements com.libraryA.Bar, com.libraryB.Moo {
// Implementation of Foo, that does NOT override equals
}
El escritor Foo
mira los supertipos, no ve ninguna implementación de iguales, y concluye que para obtener la igualdad de referencia, todo lo que necesita hacer es heredar iguales de Object
. Luego, la próxima semana, el mantenedor de la biblioteca para Bar" helpfully " agrega un valor predeterminado equals
aplicación. Ooops! Ahora la semántica de Foo
se ha roto por una interfaz en otro dominio de mantenimiento "de manera útil" añadiendo un valor predeterminado para un método común.
Se supone que los valores predeterminados son valores predeterminados. Agregar un valor predeterminado a una interfaz donde no habÃa ninguno (en cualquier lugar de la jerarquÃa) no deberÃa afectar la semántica de las clases de implementación concretas. Pero si los valores predeterminados pudieran "anular" los métodos de objeto, eso no serÃa cierto.
Asà que, si bien parece una caracterÃstica inofensiva, de hecho, es bastante dañino: agrega mucha complejidad para una pequeña expresividad incremental, y lo hace demasiado fácil para que los cambios bien intencionados e inofensivos en las interfaces compiladas por separado socaven la semántica pretendida de implementar clases.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-04 21:52:46
Está prohibido definir métodos predeterminados en interfaces para métodos en java.lang.Object
, ya que los métodos predeterminados nunca serÃan "accesibles".
Los métodos de interfaz predeterminados se pueden sobrescribir en las clases que implementan la interfaz y la implementación de la clase del método tiene una prioridad más alta que la implementación de la interfaz, incluso si el método se implementa en una superclase. Dado que todas las clases heredan de java.lang.Object
, los métodos en java.lang.Object
tendrÃan prioridad sobre el método predeterminado en el interfaz y ser invocado en su lugar.
Brian Goetz de Oracle proporciona algunos detalles más sobre la decisión de diseño en este post de la lista de correo.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-03 14:09:51
No veo en la cabeza de los autores del lenguaje Java, por lo que solo podemos adivinar. Pero veo muchas razones y estoy totalmente de acuerdo con ellas en esta cuestión.
La razón principal para introducir métodos predeterminados es poder agregar nuevos métodos a las interfaces sin romper la compatibilidad con versiones anteriores de implementaciones anteriores. Los métodos predeterminados también se pueden usar para proporcionar métodos de "conveniencia" sin la necesidad de definirlos en cada una de las clases implementadoras.
Ninguno de estos se aplica a toString y otros métodos de Object. En pocas palabras, los métodos predeterminados fueron diseñados para proporcionar el comportamiento predeterminado donde no hay otra definición. No proporcionar implementaciones que "compitan" con otras implementaciones existentes.
La regla de "la clase base siempre gana" también tiene sus razones sólidas. Se supone que las clases definen implementaciones reales , mientras que las interfaces definen implementaciones predeterminadas, que son débil.
Además, introducir excepciones a las reglas generales causa complejidad innecesaria y plantea otras cuestiones. Object es (más o menos) una clase como cualquier otra, entonces, ¿por qué deberÃa tener un comportamiento diferente?
Con todo, la solución que propones probablemente traerÃa más contras que pros.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-03 14:20:02
El razonamiento es muy simple, es porque Object es la clase base para todas las clases Java. Asà que incluso si tenemos el método de Object definido como método predeterminado en alguna interfaz, será inútil porque el método de Object siempre se usará. Es por eso que para evitar confusiones, no podemos tener métodos predeterminados que estén sobreescribiendo métodos de clase de objeto.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-15 08:19:37
Para dar una respuesta muy pedante, solo está prohibido definir un método default
para público método de java.lang.Object
. Hay 11 métodos a considerar, que se pueden clasificar de tres maneras para responder a esta pregunta.
- Seis de los métodos
Object
no pueden tener métodosdefault
porque sonfinal
y no pueden ser anulados en absoluto:getClass()
,notify()
,notifyAll()
,wait()
,wait(long)
, ywait(long, int)
. - Tres de los métodos
Object
no pueden tener métodosdefault
por las razones dado arriba por Brian Goetz:equals(Object)
,hashCode()
, ytoString()
. -
Dos de los métodos
Object
pueden tenerdefault
métodos, aunque el valor de tales valores predeterminados es cuestionable en el mejor de los casos:clone()
yfinalize()
.public class Main { public static void main(String... args) { new FOO().clone(); new FOO().finalize(); } interface ClonerFinalizer { default Object clone() {System.out.println("default clone"); return this;} default void finalize() {System.out.println("default finalize");} } static class FOO implements ClonerFinalizer { @Override public Object clone() { return ClonerFinalizer.super.clone(); } @Override public void finalize() { ClonerFinalizer.super.finalize(); } } }
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-02 01:25:28