¿Por qué Cloneable no está en desuso?

139

Se entiende comúnmente que la Cloneableinterfaz en Java está rota. Hay muchas razones para esto, que no mencionaré; otros ya lo hicieron. También es la posición de los propios arquitectos de Java .

Mi pregunta es por lo tanto: ¿por qué aún no ha quedado en desuso? Si el equipo central de Java ha decidido que está roto, entonces también deben haber considerado la desaprobación. ¿Cuáles son sus razones para no hacerlo (en Java 8 todavía no está en desuso )?

Kao
fuente
41
Esta pregunta no está "principalmente basada en la opinión", ya que muchos aparentemente se sienten con derecho a juzgar. Aquellos que no tienen más que una opinión sobre las razones simplemente no están calificados para responder. Sin embargo, es cierto que solo tiene una remota posibilidad de obtener una respuesta autorizada aquí. También es cierto que su pregunta no se trata de un problema solucionable que tenga, por lo que al menos está fuera del tema.
Marko Topolnik
66
@MarkoTopolnik Estoy de acuerdo en que hay algunas personas en el mundo que podrían proporcionar una respuesta autorizada, pero no creo que esa sea la prueba que aplicamos aquí. La razón de cierre establece que "las respuestas a esta pregunta tenderán a basarse casi exclusivamente en opiniones". Sospecho que ese será el caso aquí, a menos que tengamos mucha suerte.
Duncan Jones
2
Aquí está "Cómo y cuándo" en desuso de Oracle ... ( docs.oracle.com/javase/6/docs/technotes/guides/javadoc/… ) La interfaz clonable podría caer en el caso "defectuoso o altamente ineficiente" pero Es muy abierto a las opiniones.
Maxx
8
@Duncan Todavía no considero justo emitir un juicio sobre la cuestión en función de mis suposiciones sobre la falta de disciplina por parte de los respondedores . Si un usuario no sabe el motivo por el que se le pregunta, no tiene derecho a abusar del servicio de respuesta para presentar su opinión sobre el asunto.
Marko Topolnik
44
@lexicore Sí, exactamente --- y puedes apostar que han considerado a fondo esa opción y, por implicación, deben tener fuertes razones para no desaprobarla. Su propia crítica de Cloneablees ampliamente conocida.
Marko Topolnik

Respuestas:

120

Hay un error enviado en 1997 a Java Bug Database sobre agregar un clone()método Cloneable, por lo que ya no sería inútil. Se cerró con la resolución "no solucionará" y la justificación fue la siguiente:

El Comité de Revisión Técnica (TRC) de Sun consideró este problema detenidamente y recomendó no tomar ninguna otra medida que no sea mejorar la documentación de la interfaz Cloneable actual . Aquí está el texto completo de la recomendación:

Las API de clonación de objetos Java existentes son problemáticas. Hay un método de "clonación" protegido en java.lang.Object y hay una interfaz java.lang.Cloneable. La intención es que si una clase quiere permitir que otras personas la clonen, entonces debería admitir la interfaz Clonable y anular el método de clonación protegido predeterminado con un método de clonación pública. Desafortunadamente, por razones convenientemente perdidas en las brumas del tiempo, la interfaz clonable no define un método de clonación.

Esta combinación resulta en una buena cantidad de confusión. Algunas clases afirman ser compatibles con Cloneable, pero accidentalmente olvidan admitir el método de clonación. Los desarrolladores están confundidos acerca de cómo se supone que funciona Cloneable y qué se supone que debe hacer el clon.

Desafortunadamente, agregar un método de "clonación" a Cloneable sería un cambio incompatible. No romperá la compatibilidad binaria, pero romperá la compatibilidad de origen. La evidencia anecdótica sugiere que, en la práctica, hay una serie de casos en los que las clases admiten la interfaz clonable pero no proporcionan un método de clonación pública. Después de la discusión, TRC recomendó por unanimidad que NO deberíamos modificar la interfaz Cloneable existente, debido al impacto de compatibilidad.

Una propuesta alternativa era agregar una nueva interfaz java.lang.PubliclyCloneable para reflejar el propósito original de Cloneable. Por una mayoría de 5 a 2, TRC recomendó no hacerlo. La principal preocupación era que esto agregaría aún más confusión (¡incluida la confusión ortográfica!) A una imagen ya confusa.

TRC recomendó por unanimidad que agreguemos documentación adicional a la interfaz Cloneable existente para describir mejor cómo se debe usar y para describir las "mejores prácticas" para los implementadores.

Entonces, aunque esto no se trata directamente de obsoleto , la razón para no hacer que Cloneable sea "obsoleto" es que el Comité de Revisión Técnica decidió que modificar la documentación existente será suficiente para que esta interfaz sea útil. Y así lo hicieron. Hasta Java 1.4,Cloneable se documentó de la siguiente manera:

Una clase implementa la interfaz Cloneable para indicar al método Object.clone () que es legal para ese método hacer una copia de campo por campo de instancias de esa clase.

Los intentos de clonar instancias que no implementan la interfaz Cloneable dan como resultado la excepción CloneNotSupportedException.

La interfaz Cloneable no declara métodos.

Desde Java 1.4 (que se lanzó en febrero de 2002) hasta la edición actual (Java 8) se ve así:

Una clase implementa la interfaz Cloneable para indicar al método Object.clone () que es legal para ese método hacer una copia de campo por campo de instancias de esa clase. Al invocar el método de clonación de Object en una instancia que no implementa la interfaz Cloneable, se genera la excepción CloneNotSupportedException.

Por convención, las clases que implementan esta interfaz deben anular Object.clone (que está protegido) con un método público. Consulte Object.clone () para obtener detalles sobre cómo anular este método.

Tenga en cuenta que esta interfaz no contiene el método de clonación. Por lo tanto, no es posible clonar un objeto simplemente por el hecho de que implementa esta interfaz. Incluso si el método de clonación se invoca reflexivamente, no hay garantía de que tenga éxito.

Kao
fuente
3
y sabes por qué el clonemétodo estaba en Objectprimer lugar?
njzk2
8
@ njzk2 Esa es una parte esencial del mecanismo: es el método que realiza la magia extralingüística de bajo nivel de copiar la imagen del objeto bit por bit.
Marko Topolnik el
3
@Unheilig Esa magia es algo que no puedes replicar con un constructor de copias: Object#clone()produce una instancia de la misma clase que la original sin que esa clase se conozca en tiempo de compilación.
Marko Topolnik
44
@AVolpe: Eso no solucionaría la incompatibilidad de origen mencionada en la respuesta "donde las clases admiten la interfaz clonable pero no proporcionan un método de clonación público". En particular, las clases que proporcionan un método de clonación no pública se romperían.
Louis Wasserman
2
Buena historia Aquí hay un enlace directo a la base de datos de errores. He agregado más historia allí, y he citado partes de ella en mi respuesta .
Stuart Marks
64

La respuesta corta a "¿por qué no está en Cloneabledesuso?" (o de hecho, por qué no está en Xdesuso, para cualquierX ) es que no se ha prestado mucha atención a despreciarlos.

La mayoría de las cosas que han quedado en desuso recientemente quedaron en desuso porque existe un plan específico para eliminarlas. Por ejemplo, las addPropertyChangeListenery removePropertyChangeListenerlos métodos de LogManager estaban en desuso en Java SE 8 con la intención de dejarlas en Java SE 9. (La razón es que innecesariamente complicadas interdependencias de los módulos.) De hecho, estas API ya se han eliminado desde la primera JDK 9 desarrollo Construye. (Tenga en cuenta que también se eliminaron llamadas de escucha de cambio de propiedad similares Pack200; consulte JDK-8029806 ).

No existe un plan similar para Cloneabley para Object.clone().

Una respuesta más larga implicaría discutir más preguntas, como qué podría esperarse que suceda con estas API, qué costos o beneficios acumularían la plataforma si se desaprobaran, y qué se comunica a los desarrolladores cuando una API se desaprueba. Exploré este tema en mi reciente charla de JavaOne, Deuda y desaprobación . (Diapositivas disponibles en ese enlace; video aquí .) Resulta que el JDK en sí mismo no ha sido muy consistente en su uso de la degradación. Se ha utilizado para significar varias cosas diferentes, que incluyen, por ejemplo,

  • Esto es peligroso y debe ser consciente de los riesgos de su utilización (ejemplo: Thread.stop(), Thread.resume(), y Thread.suspend()).

  • Esto se eliminará en una versión futura

  • Esto es obsoleto y es una buena idea que use algo diferente (ejemplo: muchos de los métodos en java.util.Date)

Todos estos son significados distintos, y diferentes subconjuntos de ellos se aplican a diferentes cosas que están en desuso. Y algunos subconjuntos de ellos se aplican a cosas que no están en desuso (pero que tal vez deberían estar en desuso).

Cloneabley Object.clone()están "rotos" en el sentido de que tienen fallas de diseño y son difíciles de usar correctamente. Sin embargo, clone()sigue siendo la mejor manera de copiar matrices, y la clonación tiene una utilidad limitada para hacer copias de instancias de clases que se implementan cuidadosamente. Eliminar la clonación sería un cambio incompatible que rompería muchas cosas. Una operación de clonación podría implementarse de una manera diferente, pero probablemente sería más lenta queObject.clone() .

Sin embargo, para la mayoría de las cosas, un constructor de copia es preferible a la clonación. Entonces quizás marcandoCloneable sea ​​apropiado como "obsoleto" o "reemplazado" o algo similar. Esto les diría a los desarrolladores que probablemente quieran buscar en otro lado, pero no indicaría que el mecanismo de clonación podría eliminarse en una versión futura. Desafortunadamente, no existe tal marcador.

Tal como están las cosas, la "desaprobación" parece implicar la eliminación eventual, a pesar del hecho de que una cantidad cada vez menor de características desaprobadas se han eliminado alguna vez, por lo que la desaprobación no parece justificada por el mecanismo de clonación. Quizás en el futuro se pueda aplicar una marca alternativa que dirija a los desarrolladores a utilizar mecanismos alternativos.

ACTUALIZAR

Agregué un historial adicional al informe de error . Frank Yellin, uno de los primeros implementadores de JVM y coautor de la especificación de JVM, hizo algunos comentarios en respuesta al comentario de "perdido en las brumas del tiempo" en la recomendación de TRC citada en la otra respuesta . He citado las porciones relevantes aquí; El mensaje completo está en el informe de error.

Cloneable no tiene métodos por la misma razón que Serializable no. Cloneable indica una propiedad de la clase, en lugar de decir específicamente algo sobre los métodos que admite la clase.

Antes de la reflexión, necesitábamos un método nativo para hacer una copia superficial de un Objeto. De ahí nació Object.clone (). También estaba claro que muchas clases querrían anular este método, y que no todas las clases querrían ser clonadas. Por lo tanto, Cloneable nació para indicar la intención del programador.

Entonces, en resumen. El propósito de Cloneable no era indicar que tenía un método público clone (). Fue para indicar que estaba dispuesto a ser clonado usando Object.clone (), y fue la implementación decidir si hacer público clone () o no.

Stuart Marks
fuente
3
Una buena respuesta que tiene aquí, señor. Me gusta especialmente que no solo te lances Object.clone()al fuego solo porque todos los demás lo deseen, sino que estás dispuesto a razonar y sacar a relucir las cosas buenas.
icza
2
Sin embargo, clone () sigue siendo la mejor manera de copiar matrices, y la clonación tiene una utilidad limitada para hacer copias de instancias de clases que se implementan cuidadosamente. Tenía la impresión de que con la corrección de 6428387, todas las rutas de código (clone, new / arrayCopy, Arrays.copyOf) dieron como resultado las mismas características intrínsecas. ¿Ha cambiado algo recientemente?
bestsss
2
@bestsss No creo que array.clone()sea ​​necesariamente más rápido que cualquier otra alternativa. Desde la perspectiva API, es la forma más concisa de duplicar una matriz. Arrays.copyOf(array, newlen)se acerca, pero requiere un parámetro de longitud, que es redundante si no está cambiando la longitud.
Stuart Marks
2
@Holger Sí, hasta donde podemos ver, esta es la primera eliminación real de una API desde 1.1. Tenga en cuenta también que, aunque estamos de acuerdo en que Thread.suspend()y Thread.stop()(sin argumentos) son peligrosos, probablemente no se eliminarán, ni se cambiarán para lanzar una excepción incondicional, ¡porque la gente realmente los usa! Presumiblemente están dispuestos a asumir el riesgo. Uno de los factores atenuantes con los oyentes de cambio de propiedad es que se usaron muy raramente, por lo que el impacto de eliminarlos es pequeño.
Stuart Marks
2
@Holger Conceptualmente java.beanspodría hacerse independiente java.desktopya que los beans son solo una API de biblioteca para propiedades. Desafortunadamente, si profundiza en la API de beans, hay muchas dependencias en AWT. La implementación tiene aún más. Ciertamente, podría ser posible extraerlos, pero hacer eso parece mucho más trabajo que, por ejemplo, desenredar la tala de los frijoles. Todo el esfuerzo de modularización se trata de hacer este desenredado; indudablemente se podría hacer más, pero luego Jigsaw tomaría aún más tiempo.
Stuart Marks el
-1

¿Por qué todavía no está en desuso?

Porque el PCJ no ha creído conveniente hacerlo, y puede que nunca lo haga. Pregúntales. Estás preguntando en el lugar equivocado.

¿Cuáles son las razones para mantener esto en la API de Java?

Nadie eliminará nada de la API de Java, debido al requisito de compatibilidad con versiones anteriores. La última vez que sucedió fue el cambio en el modelo de evento AWT entre 1.0 y 1.1 en 1996/7.

Marqués de Lorne
fuente
17
Se eliminaron (efectivamente) Thread.stop(Throwable)cambiando su contrato para lanzar siempre UnsupportedOperationExceptiona la persona que llama (¡no al hilo objetivo!).
Marko Topolnik
22
¿Qué ocurrió al mismo tiempo? La eliminación de Thread.stop(Throwable)la funcionalidad de '' ocurrió en Java 8. De todos modos, el consejo no calificado de "preguntarles" está mal porque hoy el arquitecto jefe de Java es un miembro activo en Stack Overflow. Simplemente no se molesta en responder nada más que preguntas relacionadas con Streams.
Marko Topolnik el
13
Además, la pregunta de OP no se trata de la eliminación , sino de la desaprobación , y claramente la desaprobación ha estado sucediendo todo el tiempo.
Marko Topolnik el
17
@EJP No estoy preguntando si Cloneableserá eliminado de la API de Java. Estoy preguntando por qué no va a ser depravado. Y creo que este es un lugar perfecto para preguntar.
Kao
55
@VaheHarutyunyan Gracias por el agradecimiento, pero no soy un arquitecto de Java. Soy ingeniero en el grupo JDK de Oracle, que mantiene estas cosas.
Stuart Marks