Buscar..


Sintaxis

  • val implícito x: T = ???

Observaciones

Las clases implícitas permiten que se agreguen métodos personalizados a los tipos existentes, sin tener que modificar su código, enriqueciendo así los tipos sin necesidad de controlar el código.

El uso de tipos implícitos para enriquecer una clase existente a menudo se denomina patrón de "enriquecer mi biblioteca".

Restricciones en las clases implícitas

  1. Las clases implícitas solo pueden existir dentro de otra clase, objeto o rasgo.
  2. Las clases implícitas solo pueden tener un parámetro de constructor primario no implícito.
  3. Puede que no haya otra definición de objeto, clase, rasgo o miembro de clase dentro del mismo ámbito que tenga el mismo nombre que la clase implícita.

Conversión implícita

Una conversión implícita permite al compilador convertir automáticamente un objeto de un tipo a otro tipo. Esto permite que el código trate un objeto como un objeto de otro tipo.

case class Foo(i: Int)

// without the implicit
Foo(40) + 2    // compilation-error (type mismatch)

// defines how to turn a Foo into an Int
implicit def fooToInt(foo: Foo): Int = foo.i

// now the Foo is converted to Int automatically when needed
Foo(40) + 2    // 42

La conversión es unidireccional: en este caso, no puede convertir 42 nuevo a Foo(42) . Para ello, se debe definir una segunda conversión implícita:

implicit def intToFoo(i: Int): Foo = Foo(i)

Tenga en cuenta que este es el mecanismo por el cual se puede agregar un valor flotante a un valor entero, por ejemplo.

Las conversiones implícitas se deben usar con moderación porque ocultan lo que está sucediendo. Es una práctica recomendada utilizar una conversión explícita a través de una llamada de método, a menos que haya una ganancia de legibilidad tangible al usar una conversión implícita.

No hay un impacto significativo en el rendimiento de las conversiones implícitas.

Scala importa automáticamente una variedad de conversiones implícitas en scala.Predef , incluidas todas las conversiones de Java a Scala y viceversa. Estos están incluidos por defecto en cualquier compilación de archivos.

Parámetros implícitos

Los parámetros implícitos pueden ser útiles si un parámetro de un tipo debe definirse una vez en el alcance y luego aplicarse a todas las funciones que usan un valor de ese tipo.

Una llamada de función normal se parece a esto:

// import the duration methods
import scala.concurrent.duration._

// a normal method:
def doLongRunningTask(timeout: FiniteDuration): Long = timeout.toMillis

val timeout = 1.second
// timeout: scala.concurrent.duration.FiniteDuration = 1 second

// to call it
doLongRunningTask(timeout) // 1000

Ahora digamos que tenemos algunos métodos que tienen una duración de tiempo de espera, y queremos llamar a todos esos métodos utilizando el mismo tiempo de espera. Podemos definir el tiempo de espera como una variable implícita.

// import the duration methods
import scala.concurrent.duration._

// dummy methods that use the implicit parameter
def doLongRunningTaskA()(implicit timeout: FiniteDuration): Long = timeout.toMillis
def doLongRunningTaskB()(implicit timeout: FiniteDuration): Long = timeout.toMillis

// we define the value timeout as implicit
implicit val timeout: FiniteDuration = 1.second

// we can now call the functions without passing the timeout parameter
doLongRunningTaskA() // 1000
doLongRunningTaskB() // 1000

La forma en que funciona es que el compilador de scalac busca un valor en el ámbito que está marcado como implícito y cuyo tipo coincide con el del parámetro implícito. Si encuentra uno, lo aplicará como parámetro implícito.

Tenga en cuenta que esto no funcionará si define dos o incluso más implicaciones del mismo tipo en el ámbito.

Para personalizar el mensaje de error, utilice el implicitNotFound anotación del tipo:

@annotation.implicitNotFound(msg = "Select the proper implicit value for type M[${A}]!")
case class M[A](v: A) {}

def usage[O](implicit x: M[O]): O = x.v

//Does not work because no implicit value is present for type `M[Int]`
//usage[Int]   //Select the proper implicit value for type M[Int]!
implicit val first: M[Int] = M(1)
usage[Int]     //Works when `second` is not in scope
implicit val second: M[Int] = M(2)
//Does not work because more than one implicit values are present for the type `M[Int]`
//usage[Int]   //Select the proper implicit value for type M[Int]!

Un tiempo de espera es un caso de uso habitual para esto, o por ejemplo, en Akka, el sistema Actor es (la mayoría de las veces) siempre el mismo, por lo que generalmente se pasa implícitamente. Otro caso de uso sería diseño de la biblioteca, más comúnmente con las bibliotecas de PF que se basan en clases de tipos (como scalaz , gatos o éxtasis ).

Generalmente se considera una mala práctica usar parámetros implícitos con tipos básicos como Int , Long , String , etc., ya que creará confusión y hará que el código sea menos legible.

Clases Implícitas

Las clases implícitas permiten agregar nuevos métodos a las clases previamente definidas.

La clase String no tiene método sin withoutVowels . Esto se puede agregar así:

object StringUtil {
  implicit class StringEnhancer(str: String) {
    def withoutVowels: String = str.replaceAll("[aeiou]", "")
  }
}

La clase implícita tiene un único parámetro de constructor ( str ) con el tipo que le gustaría extender ( String ) y contiene el método que le gustaría "agregar" al tipo ( withoutVowels ). Los métodos recién definidos ahora se pueden usar directamente en el tipo mejorado (cuando el tipo mejorado está en el alcance implícito):

import StringUtil.StringEnhancer // Brings StringEnhancer into implicit scope

println("Hello world".withoutVowels) // Hll wrld

Bajo el capó, las clases implícitas definen una conversión implícita del tipo mejorado a la clase implícita, como esto:

implicit def toStringEnhancer(str: String): StringEnhancer = new StringEnhancer(str)

Las clases implícitas a menudo se definen como clases de valor para evitar la creación de objetos de tiempo de ejecución y, por lo tanto, eliminar la sobrecarga del tiempo de ejecución:

implicit class StringEnhancer(val str: String) extends AnyVal {
    /* conversions code here */
}

Con la definición mejorada anterior, no es necesario crear una nueva instancia de StringEnhancer cada vez que se invoca el método withoutVowels .

Resolución de parámetros implícitos usando 'implícitamente'

Suponiendo una lista de parámetros implícitos con más de un parámetro implícito:

case class Example(p1:String, p2:String)(implicit ctx1:SomeCtx1, ctx2:SomeCtx2)

Ahora, asumiendo que una de las instancias implícitas no está disponible ( SomeCtx1 ) mientras que todas las demás instancias implícitas necesarias están dentro del alcance, para crear una instancia de la clase, se debe proporcionar una instancia de SomeCtx1 .

Esto se puede hacer al mismo tiempo que se conserva una instancia implícita dentro del alcance utilizando la palabra clave implicitly :

Example("something","somethingElse")(new SomeCtx1(), implicitly[SomeCtx2])

Implicados en el REPL

Para ver todas las implicits en el alcance durante una sesión REPL:

scala> :implicits

Para incluir también las conversiones implícitas definidas en Predef.scala :

scala> :implicits -v

Si uno tiene una expresión y desea ver el efecto de todas las reglas de reescritura que se le aplican (incluidas las implícitas):

scala> reflect.runtime.universe.reify(expr) // No quotes. reify is a macro operating directly on code.

(Ejemplo:

scala> import reflect.runtime.universe._
scala> reify(Array("Alice", "Bob", "Eve").mkString(", "))
resX: Expr[String] = Expr[String](Predef.refArrayOps(Array.apply("Alice", "Bob", "Eve")(Predef.implicitly)).mkString(", "))

)



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow