¿Cuál es la ventaja de utilizar una clase abstracta en lugar de una característica (aparte del rendimiento)? Parece que las clases abstractas pueden ser reemplazadas por rasgos en la mayoría de los casos.¿Cuál es la ventaja de utilizar clases abstractas en lugar de rasgos?
Respuesta
puedo pensar en dos diferencias
- Las clases abstractas pueden tener parámetros del constructor, así como los parámetros de tipo. Los rasgos solo pueden tener parámetros de tipo. Hubo cierta discusión de que en el futuro incluso los rasgos pueden tener parámetros de constructor
- Las clases abstractas son totalmente interoperables con Java. Puede llamarlos desde código Java sin envoltorios. Los rasgos son totalmente interoperables sólo si no contienen ningún código de aplicación
Al extender una clase abstracta, esto muestra que la subclase es de un tipo similar. Este no es necesariamente el caso cuando uso rasgos, creo.
¿Esto tiene implicaciones prácticas, o sólo hacen que el código sea más fácil de entender? – Ralf
Solo me refiero a la comprensibilidad del código. –
Las clases abstractas pueden contener comportamientos: pueden parametrizarse con argumentos constructor (que los rasgos no pueden) y representan una entidad que funciona. Los rasgos en su lugar solo representan una característica única, una interfaz de una funcionalidad.
Espero que no esté implicando que los rasgos no pueden contener el comportamiento. Ambos pueden contener código de implementación. –
@Mitch Blevins: por supuesto que no. Pueden contener código, pero cuando defines 'trait Enumerable' con muchas funciones auxiliares, no las llamaría * comportamiento * sino simplemente funcionalidad conectada con una característica. – Dario
@Dario Veo "comportamiento" y "funcionalidad" como sinónimos, por lo que considero que su respuesta es muy confusa. –
Por lo que vale, de Odersky et al Programming in Scala recomienda que, cuando dudas, utiliza rasgos. Siempre puede cambiarlos en clases abstractas más adelante si es necesario.
En Programming Scala los autores dicen que las clases abstractas hacen una relación "is-a" orientada a objetos clásica mientras que los rasgos son una escala de composición.
Hay una sección en Programación en Scala llamada "To trait, or not to trait?" que trata esta cuestión. Dado que la primera edición está disponible en línea, espero que esté bien citar todo aquí. (Cualquier programador serio Scala debe comprar el libro):
Siempre que implemente una colección reutilizable de la conducta, se le tendrá que decidir si desea utilizar un rasgo o una clase abstracta. No hay una regla firme, pero esta sección contiene algunas pautas para considerar.
Si el comportamiento no se reutilizará, conviértalo en una clase concreta. Es no es un comportamiento reutilizable después de todo.
Si se puede reutilizar en varias clases no relacionadas, conviértalo en un rasgo. Solo los rasgos se pueden mezclar en diferentes partes de la jerarquía de clases.
Si desea heredar en código de Java, utilice una clase abstracta. Dado que los rasgos con código no tienen un análogo Java cercano, tiende a ser incómodo para heredar de un rasgo en una clase Java. Heredar de una clase Scala , mientras tanto, es exactamente como heredar de una clase Java. Como una excepción, un rasgo de Scala con solo miembros abstractos traduce directamente a una interfaz Java, por lo que debe sentirse libre de definir tales rasgos , incluso si espera que el código Java herede de él. Consulte el Capítulo 29 para obtener más información sobre cómo trabajar con Java y Scala juntos.
Si va a distribuir en forma compilada, y que esperan fuera grupos escribir clases que heredan de ella, es posible que inclinarse hacia usando una clase abstracta. El problema es que cuando un rasgo gana o pierde miembro, cualquier clase que herede debe volver a compilarse, incluso si no han cambiado. Si los clientes externos solo invocan el comportamiento , en lugar de heredarlo, usar un rasgo está bien.
Si la eficiencia es muy importante, apóyese en el uso de una clase. La mayoría de los tiempos de ejecución de Java hacen que la invocación de un método virtual de un miembro de clase sea una operación más rápida que una invocación de método de interfaz. Los rasgos se compilan en las interfaces y, por lo tanto, pueden pagar una ligera sobrecarga de rendimiento. Sin embargo, debe hacer esta elección solo si sabe que el rasgo en cuestión constituye un cuello de botella de rendimiento y tiene evidencia de que al usar una clase en realidad soluciona el problema.
Si aún no conoce, después de considerar lo anterior, comience por haciéndolo como un rasgo. Siempre puede cambiarlo más tarde y, en general, usando un rasgo mantiene abiertas más opciones.
Como @Mushtaq Ahmed mencionó, un rasgo no puede tener ningún parámetro pasado al constructor primario de una clase.
Otra diferencia es el tratamiento de super
.
La otra diferencia entre las clases y los rasgos es que, mientras que en las clases, las llamadas a
super
están vinculadas estáticamente, en rasgos, están vinculadas dinámicamente. Si escribesuper.toString
en una clase, sabrá exactamente qué método de implementación se invocará. Sin embargo, cuando escribes lo mismo en un rasgo, la implementación del método para invocar a la super llamada no está definida cuando defines el rasgo.
Consulte el resto de Chapter 12 para obtener más información.
Edición 1 (2013):
Hay una diferencia sutil en las clases abstractas manera se comporta en comparación con los rasgos. Una de las reglas de linealización es que conserva la jerarquía de herencia de las clases, lo que tiende a empujar las clases abstractas más adelante en la cadena, mientras que los rasgos pueden mezclarse felizmente. En ciertas circunstancias, es preferible estar en la última posición de la linealización de clase , así que las clases abstractas podrían usarse para eso. Ver constraining class linearization (mixin order) in Scala.
Editar 2 (2018):
Como de Scala 2.12, el comportamiento de la compatibilidad binaria de rasgo ha cambiado. Antes de 2.12, agregar o quitar un miembro al rasgo requería la recopilación de todas las clases que heredan el rasgo, incluso si las clases no han cambiado. Esto se debe a la forma en que los rasgos fueron codificados en JVM.
A partir de Scala 2.12, rasgos compile to Java interfaces, por lo que el requisito se ha relajado un poco.Si el rasgo hace cualquiera de los siguientes, sus subclases aún requieren recompilación:
- campos que definen (
val
ovar
, pero una constante es bien -final val
sin tipo de resultado)- de llamar a super
- inicializador declaraciones en el cuerpo
- extendiendo una clase
- confiando en la linealización para encontrar implementaciones en el supertrato correcto
Pero no es el rasgo, ahora puede actualizarlo sin romper la compatibilidad binaria.
Adición muy importante: una clase puede heredar de múltiples características, pero solo una clase abstracta. Creo que esta debería ser la primera pregunta que hace un desarrollador al considerar cuál usar en casi todos los casos. – BAR
'Si los clientes externos solo invocan el comportamiento, en lugar de heredarlo, entonces usar un rasgo está bien '- ¿Podría alguien explicar cuál es la diferencia aquí? 'extends' vs' with'? – 0fnt
@ 0fnt Su distinción no es acerca de extends vs with. Lo que está diciendo es que si solo mezclas el rasgo dentro de la misma compilación, los problemas de compatibilidad binaria no se aplican. Sin embargo, si su API está diseñada para permitir a los usuarios mezclar los rasgos, entonces tendrá que preocuparse por la compatibilidad binaria. –
Aparte del hecho de que no puede extender directamente múltiples clases abstractas, pero puede mezclar múltiples rasgos en una clase, vale la pena mencionar que los rasgos son apilables, ya que las llamadas super en un rasgo están vinculadas dinámicamente (se refiere a una clase o rasgo mezclado antes del actual).
De la respuesta de Thomas en Difference between Abstract Class and Trait:
trait A{
def a = 1
}
trait X extends A{
override def a = {
println("X")
super.a
}
}
trait Y extends A{
override def a = {
println("Y")
super.a
}
}
scala> val xy = new AnyRef with X with Y
xy: java.lang.Object with X with Y = [email protected]
scala> xy.a
Y
X
res0: Int = 1
scala> val yx = new AnyRef with Y with X
yx: java.lang.Object with Y with X = [email protected]
scala> yx.a
X
Y
res1: Int = 1
- Una clase puede heredar de múltiples rasgos pero sólo una clase abstracta.
- Las clases abstractas pueden tener parámetros de constructor así como también parámetros de tipo. Los rasgos solo pueden tener parámetros de tipo. Por ejemplo, no puede decir rasgo t (i: Int) {}; el parámetro i es ilegal.
- Las clases abstractas son totalmente interoperables con Java. Puede llamarlos desde código Java sin envoltorios. Los rasgos son totalmente interoperables solo si no contienen ningún código de implementación.
- 1. ¿Es pitónico utilizar interfaces/clases base abstractas?
- 2. ¿Cuál es la ventaja de la interfaz sobre las clases abstractas?
- 3. ¿Cuál es la ventaja de utilizar NginX para Node.js?
- 4. ¿Cuál es la ventaja de utilizar la codificación Base64?
- 5. ¿cuál es la ventaja de utilizar "importación estática"?
- 6. ¿Cuál es la utilidad de los constructores públicos en clases abstractas en C#?
- 7. ¿Cuál es la ventaja de usar un ObjectId en lugar de un String simple?
- 8. ¿Cuál es la ventaja de tener clases internas estáticas públicas de una interfaz/clase?
- 9. ventaja de utilizar (function() {...})() en JavaScript
- 10. ¿Cuál es la ventaja de Jikes RVM
- 11. ¿Constructores en clases abstractas?
- 12. ¿Cuál es la ventaja de usar `exec` sobre` type() `al crear clases en tiempo de ejecución?
- 13. ¿Cuál es la ventaja de establecer DataContext en código en lugar de XAML?
- 14. Interfaces vs. clases abstractas
- 15. Grandes clases base abstractas
- 16. ¿Cuál es la ventaja del comando de rebase en Mercurial?
- 17. ¿Múltiples clases abstractas derivadas?
- 18. ¿Cuál es la ventaja de usar memset() en C
- 19. ¿Las clases abstractas en Scala realmente funcionan mejor que los rasgos?
- 20. ¿Cuál es la forma correcta de sobrecargar a los operadores en clases base abstractas?
- 21. ¿Cuál es la ventaja de usar arrayWithCapacity en lugar de usar array?
- 22. ¿cuál es la ventaja de usar put y delete en lugar de simplemente get y post
- 23. Cómo crear propiedades abstractas en las clases abstractas de Python
- 24. Alternativas a java.lang.reflect.Proxy para la creación de proxies de clases abstractas (en lugar de las interfaces)
- 25. En Java, ¿cuál es la ventaja de utilizar BufferedWriter para anexar a un archivo?
- 26. Clases abstractas en relaciones GORM
- 27. ¿Cuál es la ventaja de esta llamada de función indirecta?
- 28. JAXB y clases abstractas
- 29. ¿Cuál es la ventaja de utilizar una columna INCLUDE con un índice de SQL Server?
- 30. ¿Cuál es la ventaja de utilizar el almacén de sesión predeterminado sin conexión?
Adición muy importante: una clase puede heredar de múltiples características, pero solo una clase abstracta. Creo que esta debería ser la primera pregunta que hace un desarrollador al considerar cuál usar en casi todos los casos. – BAR
salvavidas: "Los rasgos son totalmente interoperables solo si no contienen ningún código de implementación" –
abstracto: cuando los comportamientos colectivos definen o conducen a un objeto (rama del objeto) pero aún no están compuestos para ser un objeto (listo). Rasgos, cuando necesita inducir capacidades, es decir, las capacidades nunca se originan con la creación de un objeto, evoluciona o se requiere cuando un objeto sale del aislamiento y tiene que comunicarse. –