Un auto-tipo para un rasgo A:
trait B
trait A { this: B => }
dice que " Ano se puede mezclar en una clase concreta que no se extienda B" .
Por otro lado, lo siguiente:
trait B
trait A extends B
dice que "cualquier clase (concreta o abstracta) que se mezcle Atambién se mezclará en B" .
¿Estas dos afirmaciones no significan lo mismo? El auto-tipo parece servir solo para crear la posibilidad de un simple error en tiempo de compilación.
¿Qué me estoy perdiendo?

trait A[Self] {this: Self => }es legal,trait A[Self] extends Selfno lo es.Respuestas:
Se utiliza principalmente para la inyección de dependencia , como en el patrón de pastel. Existe un gran artículo que cubre muchas formas diferentes de inyección de dependencia en Scala, incluido el Patrón de pastel. Si buscas en Google "Cake Pattern and Scala", obtendrás muchos enlaces, incluidas presentaciones y videos. Por ahora, aquí hay un enlace a otra pregunta .
Ahora, en cuanto a cuál es la diferencia entre un auto tipo y extender un rasgo, eso es simple. Si dices
B extends A, entoncesBes unA. Cuando usas auto-tipos,Brequiere unA. Hay dos requisitos específicos que se crean con autotipos:Bse prolonga, entonces usted está obligado a mezclar en unaA.A.Considere los siguientes ejemplos:
Si
Tweeterfuera una subclase deUser, no habría error. En el código anterior, que requiere unaUsercada vez queTweeterse utiliza, sin embargo, unUserno se proporcionó aWrong, así que conseguimos un error. Ahora, con el código anterior todavía en el alcance, considere:Con
Right,Userse cumple el requisito de mezclar a . Sin embargo, el segundo requisito mencionado anteriormente no se cumple: la carga de la implementaciónUsersigue siendo para las clases / rasgos que se extiendenRight.Con
RightAgainambos requisitos se cumplen. AUsery una implementación deUserse proporcionan.Para casos de uso más prácticos, consulte los enlaces al comienzo de esta respuesta. Pero, con suerte ahora lo entiendes.
fuente
trait WarmerComponentImpl extends SensorDeviceComponent with OnOffDeviceComponent? Eso provocaríaWarmerComponentImpltener esas interfaces. Estarían disponibles para cualquier cosa que se extiendaWarmerComponentImpl, lo cual es claramente incorrecto, ya que no es unSensorDeviceComponent, ni unOnOffDeviceComponent. Como tipo automático, estas dependencias están disponibles exclusivamente paraWarmerComponentImpl. AListpodría usarse como unArrayy viceversa. Pero simplemente no son lo mismo.thiscon tipos propios es algo que menosprecio, ya que no tiene ninguna razón para ocultar el originalthis.self: Dep1 with Dep2 =>.Los tipos propios le permiten definir dependencias cíclicas. Por ejemplo, puedes lograr esto:
El uso de la herencia
extendsno lo permite. Tratar:En el libro de Odersky, mire la sección 33.5 (Creación del capítulo de la UI de la hoja de cálculo) donde menciona:
Espero que esto ayude.
fuente
Una diferencia adicional es que los autotipos pueden especificar tipos que no son de clase. Por ejemplo
El auto tipo aquí es un tipo estructural. El efecto es decir que cualquier cosa que se mezcle en Foo debe implementar una unidad de retorno del método "cerrar" sin argumentos. Esto permite mezclas seguras para la tipificación de patos.
fuente
abstract class A extends {def close:Unit}es equivalente aabstract class A {def close:Unit}. Por lo tanto, no involucra tipos estructurales.La sección 2.3 "Anotaciones de autotipo" del papel Scala original de Martin Odersky. Las abstracciones de componentes escalables en realidad explican muy bien el propósito del autotipo más allá de la composición mixin: proporcionar una forma alternativa de asociar una clase con un tipo abstracto.
El ejemplo dado en el documento fue el siguiente, y no parece tener un corresponsal de subclase elegante:
fuente
Otra cosa que no se ha mencionado: debido a que los autotipos no son parte de la jerarquía de la clase requerida, se pueden excluir de la coincidencia de patrones, especialmente cuando se compara exhaustivamente con una jerarquía sellada. Esto es conveniente cuando desea modelar comportamientos ortogonales como:
fuente
TL; DR resumen de las otras respuestas:
Los tipos que extiende están expuestos a los tipos heredados, pero los autotipos no
Por ejemplo: le
class Cow { this: FourStomachs }permite utilizar métodos solo disponibles para rumiantes, comodigestGrass. Sin embargo, los rasgos que extienden Cow no tendrán tales privilegios. Por otro lado,class Cow extends FourStomachsexpondrádigestGrassa quien seaextends Cow.los auto-tipos permiten dependencias cíclicas, extender otros tipos no
fuente
Comencemos con la dependencia cíclica.
Sin embargo, la modularidad de esta solución no es tan buena como podría parecer en un principio, ya que puede anular los tipos propios de esta manera:
Sin embargo, si anula un miembro de tipo automático, pierde el acceso al miembro original, al que aún se puede acceder a través de super usando la herencia. Entonces, lo que realmente se gana con el uso de la herencia es:
Ahora no puedo afirmar que entiendo todas las sutilezas del patrón de pastel, pero me sorprende que el método principal para imponer la modularidad es a través de la composición en lugar de la herencia o los tipos propios.
La versión de herencia es más corta, pero la razón principal por la que prefiero la herencia sobre los tipos propios es que me resulta mucho más difícil obtener el orden de inicialización correcto con los tipos propios. Sin embargo, hay algunas cosas que puede hacer con los tipos propios que no puede hacer con la herencia. Los tipos propios pueden usar un tipo mientras que la herencia requiere un rasgo o una clase como en:
Incluso puedes hacer:
Aunque nunca podrás instanciarlo. No veo ninguna razón absoluta para no poder heredar de un tipo, pero ciertamente creo que sería útil tener clases y rasgos de constructor de ruta ya que tenemos rasgos / clases de constructor de tipo. Como lamentablemente
Tenemos esto:
O esto:
Un punto que debería empatizarse más es que los rasgos pueden extender las clases. Gracias a David Maclver por señalar esto. Aquí hay un ejemplo de mi propio código:
ScnBasehereda de la clase Swing Frame, por lo que podría usarse como un tipo automático y luego mezclarse al final (en la instanciación). Sin embargo,val geomRdebe inicializarse antes de que se use heredando rasgos. Por lo tanto, necesitamos una clase para imponer la inicialización previa degeomR. La claseScnVistapuede ser heredada de múltiples rasgos ortogonales de los cuales ellos mismos pueden ser heredados. El uso de múltiples parámetros de tipo (genéricos) ofrece una forma alternativa de modularidad.fuente
fuente
Un tipo automático le permite especificar qué tipos se pueden mezclar en un rasgo. Por ejemplo, si tiene un rasgo con un tipo automático
Closeable, ese rasgo sabe que las únicas cosas que pueden mezclarlo deben implementar laCloseableinterfaz.fuente
trait A { self:B => ... }, una declaraciónX with Asolo es válida si X extiende a B. Sí, puede decirX with A with Q, donde Q no extiende a B, pero creo que el punto de kikibobo es que X está tan restringido. ¿O me perdí algo?Actualización: una diferencia principal es que los auto-tipos pueden depender de múltiples clases (admito que es un caso de esquina). Por ejemplo, puedes tener
Esto permite agregar el
Employeemixin a cualquier cosa que sea una subclase dePersonyExpense. Por supuesto, esto solo tiene sentido si seExpenseextiendePersono viceversa. El punto es que el uso de auto-tiposEmployeepuede ser independiente de la jerarquía de las clases de las que depende. No le importa qué extiende qué: si cambia la jerarquía deExpensevsPerson, no tiene que modificarEmployee.fuente
en el primer caso, una subcaracter o subclase de B se puede mezclar con cualquier uso de A. Entonces B puede ser una característica abstracta.
fuente