¿Por qué java.lang.Object no es abstracto?

Posible duplicado:
Java: Justificación de que la clase Object no se declare abstracta

¿Por qué la clase Object, que es la clase base de ’em all en Java, no es abstracta?

He tenido esta pregunta por mucho tiempo y se pregunta aquí simplemente por curiosidad, eso es todo. Nada en mi código o en el código de nadie se está rompiendo porque no es abstracto, pero me preguntaba por qué lo hicieron concreto.

¿Por qué alguien querría una “instancia” (y no su presencia también conocida como Referencia) de esta clase de Objeto? Un caso es un código de sincronización deficiente que utiliza la instancia de un Objeto para bloquear (al menos lo usé de esta manera una vez … mi mal).

¿Hay algún uso práctico de una “instancia” de una clase Object? ¿Y cómo encaja su creación de instancias en OOP? ¿Qué hubiera pasado si lo hubieran marcado como abstracto (por supuesto después de proporcionar implementaciones a sus métodos)?

Sin los diseñadores de java.lang.Object diciéndonos, tenemos que basar nuestras respuestas en opinión. Hay algunas preguntas que se pueden hacer que pueden ayudar a aclararlo.

¿Se beneficiaría alguno de los métodos de Object de ser abstracto?

Podría argumentarse que algunos de los métodos se beneficiarían de esto. Tomemos hashCode() y equals() por ejemplo, probablemente habría habido mucha menos frustración en torno a las complejidades de estos dos si ambos se hubieran hecho abstractos. Esto requeriría que los desarrolladores averiguaran cómo deberían implementarlos, por lo que es más obvio que deberían ser consistentes (ver Java efectivo). Sin embargo, soy más de la opinión de que hashCode() , equals() y clone() pertenecen a abstracciones opt-in separadas (es decir, interfaces). Los otros métodos, wait() , notify() , finalize() , etc. son lo suficientemente complicados y / o son nativos, por lo que es mejor que ya estén implementados, y no se beneficiarían de ser abstraídos.

Así que supongo que la respuesta sería no, ninguno de los métodos de Object se beneficiaría de ser abstracto.

¿Sería un beneficio marcar la clase Object como abstracto?

Suponiendo que todos los métodos están implementados, el único efecto de marcar el resumen del Objeto es que no se puede construir (es decir, el new Object() es un error de comstackción). ¿Esto tendría un beneficio? Soy de la opinión de que el término “objeto” es en sí mismo abstracto (¿puedes encontrar algo a tu alrededor que se pueda describir totalmente como “un objeto”?), Por lo que encajaría con el paradigma orientado a objetos. Sin embargo, es del lado purista . Podría argumentarse que forzar a los desarrolladores a elegir un nombre para cualquier subclase concreta, incluso las vacías, dará como resultado un código que exprese mejor su intención. Creo que, para ser totalmente correctos en términos del paradigma, Object debe marcarse como abstract , pero cuando se llega a eso, no hay un beneficio real, es una cuestión de preferencia de diseño (pragmatismo vs. pureza).

¿Es la práctica de usar un Objeto simple para la sincronización una razón suficientemente buena para que sea concreta?

Muchas de las otras respuestas hablan sobre la construcción de un objeto simple para usar en la operación synchronized() . Si bien esto puede haber sido una práctica común y aceptada, no creo que sea una razón lo suficientemente buena para evitar que Object sea abstracto si los diseñadores lo quisieran. Otras respuestas han mencionado cómo tendríamos que declarar una sola subclase vacía de Object en cualquier momento que quisiéramos sincronizar en un determinado objeto, pero esto no se sostiene: se podría haber proporcionado una subclase vacía en el SDK ( java.lang.Lock o lo que sea), que podría construirse en cualquier momento que java.lang.Lock sincronizar. Hacer esto tendría el beneficio adicional de crear una statement de intenciones más sólida.

¿Hay algún otro factor que podría haberse visto afectado negativamente al hacer un resumen del Objeto?

Hay varias áreas, separadas desde el punto de vista del diseño puro, que pueden haber influido en la elección. Lamentablemente, no sé lo suficiente sobre ellos para ampliarlos. Sin embargo, no me sorprendería si alguno de estos tuviera un impacto en la decisión:

  • Actuación
  • Seguridad
  • Simplicidad de implementación de la JVM

¿Podría haber otras razones?

Se ha mencionado que puede estar relacionado con la reflexión. Sin embargo, la reflexión se introdujo después de que Object se diseñó. Entonces, si afecta la reflexión o no es discutible, no es la razón. Lo mismo para generics.

También está el punto inolvidable de que java.lang.Object fue diseñado por humanos: pueden haber cometido un error, es posible que no hayan considerado la pregunta. No hay un lenguaje sin defectos, y este puede ser uno de ellos, pero si lo es, no es muy grande. Y creo que puedo decir con seguridad, sin falta de ambición, que es poco probable que participe en el diseño de una parte clave de una tecnología tan ampliamente utilizada, especialmente una que duró 15 (?) Años y sigue siendo fuerte, por lo que este no debe ser considerado una crítica.

Habiendo dicho eso, lo habría resumido ;-p

Resumen
Básicamente, hasta donde yo lo veo, la respuesta a ambas preguntas “¿Por qué es concreto java.lang.Object?” o (si así fuera) “¿Por qué java.lang.Object es abstracto?” es … “¿Por qué no?”.

Las instancias java.lang.Object de java.lang.Object se utilizan normalmente en escenarios de locking / sincronización y eso es práctica aceptada.

Además, ¿cuál sería la razón para que sea abstracto? Porque no es totalmente funcional en sí mismo como una instancia? ¿Podría realmente hacer con algunos miembros abstractos? No lo creo Entonces el argumento para hacerlo abstracto en primer lugar es inexistente. Entonces no lo es.

Toma la jerarquía clásica de animales, donde tienes una clase abstracta Animal , el razonamiento para hacer el resumen de la clase Animal es porque una instancia de Animal es efectivamente un ‘inválido’ -por falta de un animal de palabra mejor (incluso si todos sus métodos proporcionar una implementación base). Con Object , simplemente no es el caso. No hay un caso abrumador para hacerlo abstracto en primer lugar.

Puedo pensar en varios casos en los que las instancias de Object son útiles:

  • Bloqueo y sincronización, como mencionan usted y otros comentaristas. Probablemente sea un olor a código, pero he visto instancias de objetos usadas de esta manera todo el tiempo.
  • Como objetos nulos , porque equals siempre devolverá falso, excepto en la instancia misma.
  • En código de prueba, especialmente al probar clases de colección. A veces es más fácil llenar una colección o matriz con objetos ficticios en lugar de null .
  • Como instancia base para clases anónimas. Por ejemplo:
    Object o = new Object() {...code here...}

De todo lo que he leído, parece que Object no necesita ser concreto , y de hecho debería haber sido abstracto.

No solo no es necesario que sea concreto, sino que después de leerlo un poco más , estoy convencido de que Object no es abstracto está en conflicto con el modelo básico de herencia; no deberíamos permitir subclases abstractas de una clase concreta, ya que las subclases solo deberían agregar funcionalidad
Claramente este no es el caso en Java, donde tenemos subclases abstractas de Object .

Creo que probablemente debería haber sido declarado abstracto, pero una vez que se hace y se libera, es muy difícil de deshacer sin causar mucho dolor – ver Java Language Spec 13.4.1:

“Si una clase que no era abstracta se cambia para que se declare abstracta, los binarios preexistentes que intentan crear nuevas instancias de esa clase lanzarán un InstantiationError en el tiempo del enlace o (si se usa un método reflexivo) una InstantiationException en tiempo de ejecución ; dicho cambio, por lo tanto, no se recomienda para clases ampliamente distribuidas “.

De vez en cuando necesita un objeto simple que no tenga un estado propio. Aunque tales objetos parecen inútiles a primera vista, todavía tienen utilidad ya que cada uno tiene una identidad diferente. Esto es útil en varios escenarios, el más importante de los cuales es el locking: desea coordinar dos hilos. En Java lo haces usando un objeto que se usará como un candado. El objeto no necesita tener ningún estado; su mera existencia es suficiente para que se convierta en un locking:

 class MyThread extends Thread { private Object lock; public MyThread(Object l) { lock = l; } public void run() { doSomething(); synchronized(lock) { doSomethingElse(); } } } Object lock = new Object(); new MyThread(lock).start(); new MyThread(lock).start(); 

En este ejemplo, utilizamos un locking para evitar que los dos subprocesos ejecuten al mismo tiempo doSomethingElse()

Si Object fuera abstracto y necesitáramos un locking, tendríamos que crear una subclase sin agregar ningún método ni campos solo para que podamos crear una instancia de locking.

Ahora que lo pienso, he aquí una pregunta doble: supongamos que el objeto fuera abstracto, ¿definirá algún método abstracto? Supongo que la respuesta es No. En tales circunstancias, no hay mucho valor para definir la clase como abstracta.

No entiendo por qué la mayoría parece creer que sería una buena idea crear una clase completamente funcional, que implemente todos sus métodos en un resumen de uso completo.
Prefiero preguntar por qué hacerlo abstracto? ¿Hace algo que no debería? ¿Falta alguna funcionalidad que debería tener? Ambas preguntas se pueden responder con no, es una clase completamente trabajadora por sí misma, por lo que es abstracta solo lleva a personas que implementan clases vacías.

 public class UseableObject extends AbstractObject{} 

UseableObject hereda de Object abstracto y sorprende que se pueda implementar, no agrega ninguna funcionalidad y su única razón para existir es permitir el acceso a los métodos expuestos por Object.
También tengo que estar en desacuerdo con el uso en sincronización “pobre”. El uso de Objetos privados para sincronizar el acceso es más seguro que usar la sincronización (esto) y es más seguro y fácil de usar que las clases de Bloqueo de java util concurrente.

Me parece que hay una simple cuestión de practicidad aquí. Hacer un resumen de clase quita la capacidad del progtwigdor para hacer algo, es decir, para crear una instancia. No hay nada que puedas hacer con una clase abstracta que no puedas hacer con una clase concreta. (Bueno, puedes declarar funciones abstractas en él, pero en este caso no necesitamos funciones abstractas). Así que al hacerlo concreto, lo haces más flexible.

Por supuesto, si hubo algún daño activo que se hizo al hacerlo concreto, esa “flexibilidad” sería un inconveniente. Pero no puedo pensar en ningún daño activo al convertir Objeto en instanciable. (¿Es “instanciable” una palabra? Lo que sea.) Podríamos debatir si un uso dado que alguien ha hecho de una instancia de Objeto sin procesar es una buena idea. Pero incluso si pudieras convencerme de que cada uso que he visto de una instancia de Objeto sin procesar era una mala idea, eso aún no probaría que no podría haber buenos usos. Entonces, si no duele nada, y podría ayudar, incluso si no podemos pensar en una forma en la que realmente ayudaría en este momento, ¿por qué prohibirlo?

Sospecho que los diseñadores no sabían de qué manera las personas pueden usar un objeto se pueden usar en el futuro, y por lo tanto no quisieron limitar a los progtwigdores al obligarlos a crear una clase adicional donde no es necesario, por ejemplo, para mutexes, claves etc.

Creo que todas las respuestas hasta ahora olvidan cómo fue con Java 1.0. En Java 1.0, no se podía hacer una clase anónima, así que si solo querías un objeto para algún propósito (sincronización o un marcador de posición nulo), tendrías que declarar una clase para ese fin, y entonces un montón de código tendría estas clases extra para este propósito. Mucho más sencillo para permitir la creación de instancias directas de Object.

Claro, si estuviera diseñando Java hoy, podría decir que todos deberían hacer:

  Object NULL_OBJECT = new Object(){}; 

Pero esa no era una opción en 1.0.

También significa que se puede crear una instancia en una matriz. En los pre-1.5 días, esto le permitiría tener estructuras de datos genéricas. Esto todavía podría ser cierto en algunas plataformas (estoy pensando en J2ME, pero no estoy seguro)

Razones por las que el objeto debe ser concreto

  1. reflexión
    ver Object.getClass ()

  2. uso genérico (pre Java 5)

  3. comparación / salida
    vea Object.toString (), Object.equals (), Object.hashCode (), etc.

  4. sincronizacion
    ver Object.wait (), Object.notify (), etc.

A pesar de que un par de áreas han sido reemplazadas / desaprobadas, todavía había una necesidad de una clase de padres concreta para proporcionar estas características a cada clase de Java.

La clase Object se usa en la reflexión para que el código pueda invocar métodos en instancias de tipo indeterminado, es decir, ‘Object.class.getDeclaredMethods ()’. Si Object fuera abstracto, entonces el código que deseaba participar tendría que implementar todos los métodos abstractos antes de que el código del cliente pudiera usar la reflexión sobre ellos.

Según Sun, una clase abstracta es una clase que se declara abstracta; puede o no incluir métodos abstractos. Las clases abstractas no se pueden crear instancias, pero se pueden subclasificar. Esto también significa que no puede llamar a métodos o acceder a campos públicos de una clase abstracta.

Ejemplo de una clase de raíz abstracta:

 abstract public class AbstractBaseClass { public Class clazz; public AbstractBaseClass(Class clazz) { super(); this.clazz = clazz; } } 

Un hijo de nuestra AbstractBaseClass:

 public class ReflectedClass extends AbstractBaseClass { public ReflectedClass() { super(this); } public static void main(String[] args) { ReflectedClass me = new ReflectedClass(); } } 

Esto no comstackrá porque no es válido hacer referencia a ‘esto’ en un constructor a menos que llame a otro constructor en la misma clase. Puedo hacer que se compile si lo cambio a:

  public ReflectedClass() { super(ReflectedClass.class); } 

pero eso solo funciona porque ReflectedClass tiene un padre (“Objeto”) que es 1) concreto y 2) tiene un campo para almacenar el tipo para sus hijos.

Un ejemplo más típico de reflexión sería en una función miembro no estático:

 public void foo() { Class localClass = AbstractBaseClass.clazz; } 

Esto falla a menos que cambie el campo ‘clazz’ para que sea estático. Para el campo de clase de Object, esto no funcionaría porque se supone que es específico de la instancia. No tendría sentido que Object tuviera un campo de clase estático.

Ahora, intenté el siguiente cambio y funciona, pero es un poco engañoso. Todavía requiere que la clase base se extienda al trabajo.

 public void genericPrint(AbstractBaseClass c) { Class localClass = c.clazz; System.out.println("Class is: " + localClass); } public static void main(String[] args) { ReflectedClass me = new ReflectedClass(); ReflectedClass meTwo = new ReflectedClass(); me.genericPrint(meTwo); } 

Los generics previos a Java5 (como los arreglos) hubieran sido imposibles

 Object[] array = new Object[100]; array[0] = me; array[1] = meTwo; 

Las instancias deben construirse para servir como marcadores de posición hasta que se reciban los objetos reales.

Sospecho que la respuesta corta es que las clases de recolección perdieron información tipo en los días previos a los generics de Java. Si una colección no es genérica, debe devolver un Objeto concreto (y ser downcast en el tiempo de ejecución al tipo que fue anteriormente).

Dado que convertir una clase concreta en una clase abstracta rompería la compatibilidad binaria (como se señaló en el subproceso), se mantuvo la clase Object concreta. Me gustaría señalar que en ningún caso fue creado con el único propósito de la sincronización; las clases ficticias funcionan igual de bien.

La falla de diseño no incluye generics desde el principio. Una gran cantidad de críticas de diseño está dirigida a esa decisión y sus consecuencias. [oh, y la regla de subtipaje de matriz.]

No es abstracto porque cada vez que creamos una nueva clase se extiende la clase Object y, si es abstracta, debes implementar todos los métodos de la clase Object, que son gastos generales … Ya hay métodos implementados en esa clase …