自类型
自类型的意思是在一个trait中可以使用另外一个trait中定义的属性而不必去继承它。
要在特质中使用自类型,写一个标识符,跟上要混入的另一个特质,以及 =>(例如 someIdentifier: SomeOtherTrait =>)。
trait User { def username: String } trait Tweeter { this: User => // 重新赋予 this 的类型 def tweet(tweetText: String) = println(s"$username: $tweetText") } class VerifiedTweeter(val username_ : String) extends Tweeter with User { // 我们混入特质 User 因为 Tweeter 需要 def username = s"real $username_" } val realBeyoncé = new VerifiedTweeter("Beyoncé") realBeyoncé.tweet("Just spilled my glass of lemonade") // 打印出 "real Beyoncé: Just spilled my glass of lemonade"
因为我们在特质 trait Tweeter 中定义了 this: User =>,现在变量 username 可以在 tweet 方法内使用。 这也意味着,由于 VerifiedTweeter 继承了 Tweeter,它还必须混入 User(使用 with User)。
隐式参数
隐式参数由 implicit 关键字标记,在方法调用的时候,scala会去尝试获取正确的隐式类型值。
Scala查找参数的位置有两个地方:
- 首先查找可以直接访问的隐式定义和隐式参数。
- 然后,它在所有伴生对象中查找与隐式候选类型相关的有隐式标记的成员。
下面的例子定义了两个隐式类型,stringMonoid和intMonoid。
abstract class Monoid[A] { def add(x: A, y: A): A def unit: A } object ImplicitTest { implicit val stringMonoid: Monoid[String] = new Monoid[String] { def add(x: String, y: String): String = x concat y def unit: String = "" } implicit val intMonoid: Monoid[Int] = new Monoid[Int] { def add(x: Int, y: Int): Int = x + y def unit: Int = 0 } def sum[A](xs: List[A])(implicit m: Monoid[A]): A = if (xs.isEmpty) m.unit else m.add(xs.head, sum(xs.tail)) def main(args: Array[String]): Unit = { println(sum(List(1, 2, 3))) // uses IntMonoid implicitly println(sum(List("a", "b", "c"))) // uses StringMonoid implicitly } }
在 main 方法中我们调用了 sum 方法两次,并且只传入参数 xs。 Scala 会在上例的上下文范围内寻找隐式值。 第一次调用 sum 方法的时候传入了一个 List[Int] 作为 xs 的值,这意味着此处类型 A 是 Int。 隐式参数列表 m 被省略了,因此 Scala 将查找类型为 Monoid[Int] 的隐式值。
intMonoid 是一个隐式定义,可以在main中直接访问。 并且它的类型也正确,因此它会被自动传递给 sum 方法。
第二次调用 sum 方法的时候传入一个 List[String],这意味着此处类型 A 是 String。 与查找 Int 型的隐式参数时类似,但这次会找到 stringMonoid,并自动将其作为 m 传入。
隐式转换
简单点讲,隐式转换就是当需要的时候,将某个类型S转换到另外一个类型T。这是通过定义隐式函数来确定的。
下面提供了一个隐式方法 List[A] => Ordered[List[A]] 的例子。
import scala.language.implicitConversions implicit def list2ordered[A](x: List[A]) (implicit elem2ordered: A => Ordered[A]): Ordered[List[A]] = new Ordered[List[A]] { //replace with a more useful implementation def compare(that: List[A]): Int = 1 }
如果需要Ordered[List[A]] 而你传入List[A]的时候,scala会自动去寻找隐式的类型转换方法。
下面是一个从scala.Int到java.lang.Integer的转换:
import scala.language.implicitConversions implicit def int2Integer(x: Int) = java.lang.Integer.valueOf(x)
多态方法
Scala中多态是通过类型和值的参数化来实现的。 如下所示:
def listOfDuplicates[A](x: A, length: Int): List[A] = { if (length < 1) Nil else x :: listOfDuplicates(x, length - 1) } println(listOfDuplicates[Int](3, 4)) // List(3, 3, 3, 3) println(listOfDuplicates("La", 8)) // List(La, La, La, La, La, La, La, La)
上例中第一次调用方法时,我们显式地提供了类型参数 [Int]。 因此第一个参数必须是 Int 类型,并且返回类型为 List[Int]。
上例中第二次调用方法,表明并不总是需要显式提供类型参数。 编译器通常可以根据上下文或值参数的类型来推断。 在这个例子中,“La” 是一个 String,因此编译器知道 A 必须是 String。
类型推断
Scala 编译器通常可以推断出表达式的类型,因此你不必显式地声明它。
val businessName = "Montreux Jazz Café"
编译器可以发现 businessName 是 String 类型。
你也可以省略方法返回类型:
def squareOf(x: Int) = x * x
编译器可以推断出方法的返回类型为 Int,因此不需要明确地声明返回类型。
当调用 多态方法 或实例化 泛型类 时,也不必明确指定类型参数。 Scala 编译器将从上下文和实际方法的类型/构造函数参数的类型推断出缺失的类型参数。
看下面两个例子:
case class MyPair[A, B](x: A, y: B) val p = MyPair(1, "scala") // type: MyPair[Int, String] def id[T](x: T) = x val q = id(1) // type: Int
编译器使用传给 MyPair 参数的类型来推断出 A 和 B 的类型。对于 x 的类型同样如此。