|
| 1 | +--- |
| 2 | +layout: doc-page |
| 3 | +title: "Implicit Conversions" |
| 4 | +--- |
| 5 | + |
| 6 | +Implicit conversions are defined by witnesses of the `scala.Conversion` class. |
| 7 | +This class is defined in package `scala` as follows: |
| 8 | +```scala |
| 9 | +abstract class Conversion[-T, +U] extends (T => U) |
| 10 | +``` |
| 11 | +For example, here is an implicit conversion from `String` to `Token`: |
| 12 | +```scala |
| 13 | +witness of Conversion[String, Token] { |
| 14 | + def apply(str: String): Token = new KeyWord(str) |
| 15 | +} |
| 16 | +``` |
| 17 | +Using an alias this can be expressed more concisely as: |
| 18 | +```scala |
| 19 | +witness of Conversion[String, Token] = new KeyWord(_) |
| 20 | +``` |
| 21 | +An implicit conversion is applied automatically by the compiler in three situations: |
| 22 | + |
| 23 | +1. If an expression `e` has type `T`, and `T` does not conform to the expression's expected type `S`. |
| 24 | +2. In a selection `e.m` with `e` of type `T`, but `T` defines no member `m`. |
| 25 | +3. In an application `e.m(args)` with `e` of type `T`, if `T` does define |
| 26 | + some member(s) named `m`, but none of these members can be applied to the arguments `args`. |
| 27 | + |
| 28 | +In the first case, the compiler looks for a `scala.Conversion` witness that maps |
| 29 | +an argument of type `T` to type `S`. In the second and third |
| 30 | +case, it looks for a `scala.Conversion` witness that maps an argument of type `T` |
| 31 | +to a type that defines a member `m` which can be applied to `args` if present. |
| 32 | +If such a witness `C` is found, the expression `e` is replaced by `C.apply(e)`. |
| 33 | + |
| 34 | +## Examples |
| 35 | + |
| 36 | +1. The `Predef` package contains "auto-boxing" conversions that map |
| 37 | +primitive number types to subclasses of `java.lang.Number`. For instance, the |
| 38 | +conversion from `Int` to `java.lang.Integer` can be defined as follows: |
| 39 | +```scala |
| 40 | +witness int2Integer of Conversion[Int, java.lang.Integer] = |
| 41 | + java.lang.Integer.valueOf(_) |
| 42 | +``` |
| 43 | + |
| 44 | +2. The "magnet" pattern is sometimes used to express many variants of a method. Instead of defining overloaded versions of the method, one can also let the method take one or more arguments of specially defined "magnet" types, into which various argument types can be converted. E.g. |
| 45 | +```scala |
| 46 | +object Completions { |
| 47 | + |
| 48 | + // The argument "magnet" type |
| 49 | + enum CompletionArg { |
| 50 | + case Error(s: String) |
| 51 | + case Response(f: Future[HttpResponse]) |
| 52 | + case Status(code: Future[StatusCode]) |
| 53 | + } |
| 54 | + object CompletionArg { |
| 55 | + |
| 56 | + // conversions defining the possible arguments to pass to `complete` |
| 57 | + // these always come with CompletionArg |
| 58 | + // They can be invoked explicitly, e.g. |
| 59 | + // |
| 60 | + // CompletionArg.fromStatusCode(statusCode) |
| 61 | + |
| 62 | + witness fromString of Conversion[String, CompletionArg] = Error(_) |
| 63 | + witness fromFuture of Conversion[Future[HttpResponse], CompletionArg] = Response(_) |
| 64 | + witness fromStatusCode of Conversion[Future[StatusCode], CompletionArg] = Status(_) |
| 65 | + } |
| 66 | + import CompletionArg._ |
| 67 | + |
| 68 | + def complete[T](arg: CompletionArg) = arg match { |
| 69 | + case Error(s) => ... |
| 70 | + case Response(f) => ... |
| 71 | + case Status(code) => ... |
| 72 | + } |
| 73 | +} |
| 74 | +``` |
| 75 | +This setup is more complicated than simple overloading of `complete`, but it can still be useful if normal overloading is not available (as in the case above, since we cannot have two overloaded methods that take `Future[...]` arguments), or if normal overloading would lead to a combinatorial explosion of variants. |
0 commit comments