usar una sirve resueltos que poo para interfaces instanciar importancia ejercicios ejemplo cual creacion clases clase abstractas abstracta scala traits

scala - resueltos - para que sirve una clase abstracta



¿Cuál es la ventaja de usar clases abstractas en lugar de rasgos? (8)

¿Cuál es la ventaja de usar una clase abstracta en lugar de un rasgo (aparte del rendimiento)? Parece que las clases abstractas pueden ser reemplazadas por rasgos en la mayoría de los casos.


  1. Una clase puede heredar de múltiples rasgos pero solo una clase abstracta.
  2. Las clases abstractas pueden tener parámetros de constructor así como también parámetros de tipo. Los rasgos pueden tener solo parámetros de tipo. Por ejemplo, no puede decir el rasgo t (i: Int) {}; el parámetro i es ilegal.
  3. Las clases abstractas son totalmente interoperables con Java. Puedes llamarlos desde código Java sin envoltorios. Los rasgos son totalmente interoperables solo si no contienen ningún código de implementación.

Aparte del hecho de que no puede extender directamente múltiples clases abstractas, pero puede combinar múltiples rasgos en una clase, vale la pena mencionar que los rasgos son apilables, ya que las súper llamadas en un rasgo están vinculadas dinámicamente (se refiere a una clase o rasgo mezclado antes el actual).

De la respuesta de Thomas en Diferencia entre clase abstracta y rasgo :

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 = $anon$1@6e9b6a 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 = $anon$1@188c838 scala> yx.a X Y res1: Int = 1


Cuando se extiende una clase abstracta, esto muestra que la subclase es de un tipo similar. Este no es necesariamente el caso cuando se usan rasgos, creo.


En Programación Scala, los autores dicen que las clases abstractas forman una relación clásica orientada a objetos "is-a", mientras que los rasgos son una forma de Scala de composición.


Hay una sección en Programación en Scala llamada "¿Para caracterizar o no para caracterizar?" que aborda esta cuestión. Ya que la 1ª edición está disponible en línea, espero que esté bien citar todo aquí. (Cualquier programador serio de Scala debería comprar el libro):

Cada vez que implemente una colección reutilizable de comportamiento, tendrá que decidir si desea usar un rasgo o una clase abstracta. No hay una regla firme, pero esta sección contiene algunas pautas a considerar.

Si el comportamiento no se reutiliza , conviértalo en una clase concreta. No es un comportamiento reutilizable después de todo.

Si se puede reutilizar en varias clases no relacionadas , conviértalo en un rasgo. Solo se pueden mezclar rasgos en diferentes partes de la jerarquía de clases.

Si desea heredarlo de código Java , use una clase abstracta. Dado que los rasgos con código no tienen un análogo de Java cercano, tiende a ser incómodo heredar de un rasgo en una clase de Java. Mientras tanto, heredar de una clase Scala es exactamente como heredar de una clase Java. Como una excepción, un rasgo de Scala con solo miembros abstractos se traduce directamente a una interfaz Java, por lo que debe sentirse libre de definir dichos rasgos incluso si espera que el código Java se herede de él. Consulte el Capítulo 29 para obtener más información sobre cómo trabajar con Java y Scala juntos.

Si planea distribuirlo en forma compilada , y espera que los grupos externos escriban las clases que se heredan de él, puede inclinarse hacia el uso de una clase abstracta. El problema es que cuando un rasgo gana o pierde un miembro, cualquier clase que herede de él debe volver a compilarse, incluso si no ha cambiado. Si los clientes externos solo llamarán al comportamiento, en lugar de heredarlo, entonces usar un rasgo está bien.

Si la eficiencia es muy importante , inclínate a usar 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 la clase sea una operación más rápida que la invocación de un método de interfaz. Los rasgos se compilan en las interfaces y, por lo tanto, pueden pagar una pequeña sobrecarga de rendimiento. Sin embargo, debe hacer esta elección solo si sabe que el rasgo en cuestión constituye un cuello de botella en el rendimiento y tiene pruebas de que el uso de una clase en realidad resuelve el problema.

Si aún no lo sabes , después de considerar lo anterior, comienza por hacerlo como un rasgo. Siempre puede cambiarlo más tarde, y en general, usar un rasgo mantiene más opciones abiertas.

Como @Mushtaq Ahmed mencionó, un rasgo no puede tener ningún parámetro pasado al constructor principal de una clase.

Otra diferencia es el tratamiento del super .

La otra diferencia entre las clases y los rasgos es que mientras que en las clases, las super llamadas están vinculadas estáticamente, en los rasgos, están vinculadas dinámicamente. Si escribe super.toString en una clase, sabe exactamente qué implementación de método se invocará. Sin embargo, cuando escribe lo mismo en un rasgo, la implementación del método a invocar para la súper llamada no está definida cuando se define el rasgo.

Vea el resto del Capítulo 12 para más detalles.

Edición 1 (2013):

Hay una diferencia sutil en la forma en que se comportan las clases abstractas en comparación con los rasgos. Una de las reglas de linealización es que conserva la jerarquía de herencia de las clases, que tiende a empujar las clases abstractas más adelante en la cadena, mientras que los rasgos pueden mezclarse. En ciertas circunstancias, es preferible estar en la última posición de la linealización de clase. , así que las clases abstractas podrían ser usadas para eso. Ver restricción de la linealización de clases (orden de mezcla) en Scala .

Edición 2 (2018):

A partir de Scala 2.12, el comportamiento de compatibilidad binaria del rasgo ha cambiado. Antes de la versión 2.12, agregar o eliminar un miembro del rasgo requería la recompilación de todas las clases que heredan el rasgo, incluso si las clases no han cambiado. Esto se debe a la forma en que se codificaron los rasgos en JVM.

A partir de Scala 2.12, los rasgos se compilan en interfaces Java , por lo que el requisito se ha relajado un poco. Si el rasgo hace algo de lo siguiente, sus subclases todavía requieren la recompilación:

  • campos de definición ( val o var , pero una constante está bien - final val sin tipo de resultado)
  • llamando super
  • declaraciones inicializadoras en el cuerpo
  • extendiendo una clase
  • Confiando en la linealización para encontrar implementaciones en el supertenso derecho

Pero si el rasgo no lo hace, ahora puede actualizarlo sin romper la compatibilidad binaria.


Las clases abstractas pueden contener comportamientos: pueden parametrizarse con argumentos de constructor (que los rasgos no pueden) y representar una entidad operante. Los rasgos, en cambio, solo representan una característica única, una interfaz de una funcionalidad.


Para lo que valga, la Programación en Scala de Odersky et al recomienda que, cuando dudes, utilices rasgos. Siempre puede cambiarlos en clases abstractas más adelante si es necesario.


Puedo pensar en dos diferencias.

  1. Las clases abstractas pueden tener parámetros de constructor así como también parámetros de tipo. Los rasgos pueden tener solo parámetros de tipo. Se discutió que, en el futuro, incluso los rasgos pueden tener parámetros de constructor.
  2. Las clases abstractas son totalmente interoperables con Java. Puedes llamarlos desde código Java sin envoltorios. Los rasgos son totalmente interoperables solo si no contienen ningún código de implementación