Estoy viendo la p. 469 de "Programación en Scala" Segunda edición. Hay una línea de código que dice:¿Qué significa "<:" en Scala?
type Currency <: AbstractCurrency
No puedo descifrar lo que esto significa.
Estoy viendo la p. 469 de "Programación en Scala" Segunda edición. Hay una línea de código que dice:¿Qué significa "<:" en Scala?
type Currency <: AbstractCurrency
No puedo descifrar lo que esto significa.
Significa que se define un miembro de tipo abstracto (dentro de algún contexto, por ejemplo, un rasgo o clase), de modo que las implementaciones concretas de ese contexto deben definir ese tipo. Sin embargo, existe una restricción de que este tipo (Currency
) debe ser realmente un subtipo de AbstractCurrency
. De esta forma, el contexto abstracto puede operar con Currency
, sabiendo que comprende todas las operaciones de AbstractCurrency
.
trait AbstractCurrency {
def disappearInGreece(): Unit
}
abstract class Economy {
type Currency <: AbstractCurrency
def curr: Currency
// can call disappear... because `Currency`
// is an `AbstractCurrency`
def shake(): Unit = curr.disappearInGreece()
}
Tratar de definir Currency
sin limitaciones:
trait RadioactiveBeef
class NiceTry(val curr: RadioactiveBeef) extends Economy {
type Currency = RadioactiveBeef
}
falla.Con las limitaciones ok:
trait Euro extends AbstractCurrency
class Angela(val curr: Euro) extends Economy {
type Currency = Euro
}
Significa "debe ser un subtipo de", "debe ajustarse a", "debe extenderse". mayoría de las veces, parecería como dependiente en un parámetro genérico, como
class Home[P <: Person]
Cada hogar es apto para un cierto tipo de persona, un Home[Person]
acepta cualquier persona, puede haber Home[Student]
, Home[Elderly]
, pero no Home[Planet]
.
type Currency <: AbstractCurrency
presenta un resumen type
miembro de Currency
en el class
/trait
donde aparece. Los descendientes tendrán que elegir un tipo para que puedan ser concretos. El <: AbstractCurrencies los obliga a elegir un subtipo de AbstractCurrency
(incluido AbstractCurrency
, que está permitido).
Un miembro de tipo abstracto está muy cerca de un parámetro de tipo, del mismo modo que un miembro de valor abstracto está cerca de un parámetro de constructor.
Si tiene class A(val x: X){...}
, usted instancia el primero con new A(myX)
. Si tiene class A{val x: X; ...}
, lo instancia con el nuevo A{val x = myX }
.
Si tiene class Market[Currency <: AbstractCurrency]
, usted instancia el tipo con Market[SomeCurrencyType]
. Si tiene Market{type Currency <: AbstractCurrency}
, crea instancias con Market{type Currency = SomeCurrencyType}
. Sin embargo, Market
es un tipo válido. Significa que no sabe qué tipo de moneda utiliza este mercado (que puede restringir cómo puede usarlo).
El uso de un miembro de tipo abstracto en lugar de un parámetro de tipo puede tener beneficios, sobre todo si el parámetro de tipo no aparece en la interfaz pública del tipo, si Market
no tiene Currency
que aparece como un parámetro de la función o el resultado (no muy probable en este ejemplo). Entonces el cliente no necesita escribir Market[SomeCurrencyType]
, Market
lo hará. Por supuesto, el CurrencyType
tendrá que conocerse cuando se crea un mercado, pero luego se puede pasar simplemente como Market
.
Esta pregunta es sobre Scala, pero creo que vale la pena mencionar que el <:
[? Operatator tipo] no es exclusivo de Scala y en su lugar se origina en la teoría de tipos; ver por ejemplo el artículo sobre Subtyping en Wikipedia que hace un uso extenso de este operador.
De hecho, debido a sus fuertes conexiones con la teoría de tipos <:
no es lo único que Scala (elegantemente) tomó prestado de ella; por ejemplo, la notación term: Type
(que se ve en, por ejemplo, val foo: Foo
, def fact(x: Int): Int
) también viene del Type Theory.
Quiero agregar algunos puntos que describirán los beneficios de usabilidad de la notación < :.
Digamos, se definen las siguientes clases para su API:
case class myApiClass[param <: BaseParameter](requestBody: String, parameter: param)
Usted tiene una característica llamada BaseParameter
trait BaseParameter
Entonces, usted tiene los siguientes parámetros:
case object Parameter1 extends BaseParameter
case object Parameter2 extends BaseParameter
case object Parameter3
Ahora, cada vez que crea una instancia de myApiClass, debe pasar un objeto como argumento "parameter", cuya clase/que implementa a su vez BaseParameter (p. Parámetro1 y Parámetro2). Concretamente, esta es una afirmación, y no funcionaría si pasa el parámetro3.
Ver también [¿Qué significa "rasgo A <: B" significa?] (Http://stackoverflow.com/questions/2123663/what-does-trait-a-b-mean) – Jonas