Los métodos predeterminados son una buena herramienta nueva en nuestra caja de herramientas Java. Sin embargo, intenté escribir una interfaz que defina una defaultversión del toStringmétodo. Java me dice que esto está prohibido, ya que los métodos declarados en java.lang.Objectno pueden ser defaulteditados. ¿Por qué es este el caso?
Sé que existe la regla de "la clase base siempre gana", por lo que, de manera predeterminada (juego de palabras;), cualquier defaultimplementación de un Objectmétodo sería sobrescrita por el método de Objecttodos modos. Sin embargo, no veo ninguna razón por la cual no debería haber una excepción para los métodos de Objectla especificación. Especialmente porque toStringpodría ser muy útil tener una implementación predeterminada.
Entonces, ¿cuál es la razón por la cual los diseñadores de Java decidieron no permitir que los defaultmétodos anulen los métodos Object?
fuente

Respuestas:
Este es otro de esos problemas de diseño de lenguaje que parece "obviamente una buena idea" hasta que comienzas a cavar y te das cuenta de que en realidad es una mala idea.
Este correo tiene mucho sobre el tema (y también sobre otros temas). Hubo varias fuerzas de diseño que convergieron para llevarnos al diseño actual:
AbstractListen una interfaz), te das cuenta de que heredar equals / hashCode / toString está fuertemente vinculado a la herencia y al estado individuales, y las interfaces se heredan y no tienen estado;Ya ha tocado el objetivo "mantenerlo simple"; las reglas de herencia y resolución de conflictos están diseñadas para ser muy simples (las clases ganan interfaces, las interfaces derivadas ganan superinterfaces y cualquier otro conflicto es resuelto por la clase implementadora). Por supuesto, estas reglas podrían modificarse para hacer una excepción, pero Creo que descubrirá cuando comience a tirar de esa cuerda, que la complejidad incremental no es tan pequeña como podría pensar.
Por supuesto, hay algún grado de beneficio que justificaría una mayor complejidad, pero en este caso no está allí. Los métodos de los que estamos hablando aquí son equals, hashCode y toString. Todos estos métodos son intrínsecamente sobre el estado del objeto, y es la clase propietaria del estado, no la interfaz, quien está en la mejor posición para determinar qué significa la igualdad para esa clase (especialmente porque el contrato para la igualdad es bastante fuerte; ver Efectivo Java por algunas consecuencias sorprendentes); los escritores de interfaces están demasiado lejos.
Es fácil sacar el
AbstractListejemplo; Sería maravilloso si pudiéramos deshacernos de élAbstractListy poner el comportamiento en laListinterfaz. Pero una vez que va más allá de este ejemplo obvio, no hay muchos otros buenos ejemplos que se puedan encontrar. En la raíz,AbstractListestá diseñado para herencia única. Pero las interfaces deben estar diseñadas para la herencia múltiple.Además, imagina que estás escribiendo esta clase:
El
Fooescritor mira los supertipos, no ve la implementación de iguales y concluye que para obtener la igualdad de referencia, todo lo que necesita hacer es heredar igualesObject. Luego, la próxima semana, el responsable de mantenimiento de la biblioteca para Bar agrega "útilmente" unaequalsimplementación predeterminada . Ooops! Ahora la semántica deFoose ha roto por una interfaz en otro dominio de mantenimiento "útilmente" agregando 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 Object, eso no sería cierto.
Entonces, si bien parece una característica inofensiva, de hecho es bastante dañina: agrega mucha complejidad para poca expresividad incremental y hace que sea demasiado fácil para los cambios bien intencionados e inofensivos para interfaces compiladas por separado para socavar la semántica prevista de la implementación de clases.
fuente
hashCodeyequals, pero creo que sería muy útiltoString. Por ejemplo, algunaDisplayableinterfaz podría definir unString display()método, y ahorraría una tonelada de repeticiones para poder definir ,default String toString() { return display(); }enDisplayablelugar de requerir que cada unoDisplayableimplementetoString()o extienda unaDisplayableToStringclase base.toString()se basa solo en los métodos de la interfaz, simplemente podría agregar algo comodefault String toStringImpl()a la interfaz y anulartoString()en cada subclase para llamar a la implementación de la interfaz, un poco feo, pero funciona, y mejor que nada. :) Otra forma de hacerlo es hacer algo comoObjects.hash(),Arrays.deepEquals()yArrays.deepToString(). ¡Hice +1 en la respuesta de @ BrianGoetz!default toString()anulación en una interfaz funcional nos permitiría, al menos, hacer algo como escupir la firma de la función y la clase principal del implementador. Aún mejor, si pudiéramos aplicar algunas estrategias recursivas de String, podríamos caminar a través del cierre para obtener una muy buena descripción de la lambda, y así mejorar drásticamente la curva de aprendizaje de lambda.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 mayor prioridad que la implementación de la interfaz, incluso si el método se implementa en una superclase. Como todas las clases heredan de
java.lang.Object, los métodos enjava.lang.Objecttendrán prioridad sobre el método predeterminado en la interfaz y se invocarán en su lugar.Brian Goetz de Oracle proporciona algunos detalles más sobre la decisión de diseño en esta publicación de la lista de correo .
fuente
No veo en la cabeza de los autores del lenguaje Java, por lo que solo podemos adivinar. Pero veo muchas razones y estoy absolutamente de acuerdo con ellas en este tema.
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 pueden usarse para proporcionar métodos de "conveniencia" sin la necesidad de definirlos en cada una de las clases de implementación.
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 razones sólidas. Se supone que las clases definen implementaciones reales , mientras que las interfaces definen implementaciones predeterminadas , que son algo más débiles.
Además, la introducción de CUALQUIER excepción a las reglas generales causa una complejidad innecesaria y genera otras preguntas. El objeto es (más o menos) una clase como cualquier otra, entonces, ¿por qué debería tener un comportamiento diferente?
En general, la solución que propone probablemente traiga más inconvenientes que profesionales.
fuente
El razonamiento es muy simple, es porque Object es la clase base para todas las clases Java. Entonces, 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 anulen los métodos de la clase Object.
fuente
Para dar una respuesta muy pedante, solo está prohibido definir un
defaultmétodo para un método públicojava.lang.Object. Hay 11 métodos a considerar, que se pueden clasificar de tres maneras para responder a esta pregunta.Objectmétodos no puede tenerdefaultmétodos porque sonfinaly no puede ser anulado en absoluto:getClass(),notify(),notifyAll(),wait(),wait(long), ywait(long, int).Objectmétodos no puede tenerdefaultmétodos para las razones dadas anteriormente por Brian Goetz:equals(Object),hashCode(), ytoString().Dos de los
Objectmétodos pueden tenerdefaultmétodos, aunque el valor de estos valores predeterminados es cuestionable en el mejor de los casos:clone()yfinalize().fuente