Dưới đây là một vài lý do để sử dụng phương pháp đơn giản thú vị implicitly
.
Để hiểu / khắc phục sự cố Chế độ xem ngầm
Chế độ xem ngầm có thể được kích hoạt khi tiền tố của lựa chọn (ví dụ: xem xét, the.prefix.selection(args)
không chứa thành viên selection
có thể áp dụng cho args
(ngay cả sau khi cố gắng chuyển đổi args
với Chế độ xem ngầm). Trong trường hợp này, trình biên dịch tìm kiếm thành viên ẩn trong phạm vi hiện tại hoặc kèm theo, được kế thừa hoặc được nhập, là các Hàm từ loại đó the.prefix
sang loại có selection
các phương thức ngầm định hoặc tương đương.
scala> 1.min(2) // Int doesn't have min defined, where did that come from?
res21: Int = 1
scala> implicitly[Int => { def min(i: Int): Any }]
res22: (Int) => AnyRef{def min(i: Int): Any} = <function1>
scala> res22(1) //
res23: AnyRef{def min(i: Int): Int} = 1
scala> .getClass
res24: java.lang.Class[_] = class scala.runtime.RichInt
Chế độ xem ngầm định cũng có thể được kích hoạt khi một biểu thức không phù hợp với Loại dự kiến, như dưới đây:
scala> 1: scala.runtime.RichInt
res25: scala.runtime.RichInt = 1
Ở đây trình biên dịch tìm hàm này:
scala> implicitly[Int => scala.runtime.RichInt]
res26: (Int) => scala.runtime.RichInt = <function1>
Truy cập một tham số ngầm định được giới thiệu bởi giới hạn bối cảnh
Các tham số ngầm định được cho là một tính năng quan trọng hơn của Scala so với Chế độ xem ngầm định. Họ hỗ trợ các mẫu lớp. Thư viện tiêu chuẩn sử dụng điều này ở một vài nơi - xem scala.Ordering
và cách sử dụng nó SeqLike#sorted
. Các tham số ngầm định cũng được sử dụng để truyền các bảng kê khai và các CanBuildFrom
thể hiện của Array .
Scala 2.8 cho phép một cú pháp tốc ký cho các tham số ngầm, được gọi là Giới hạn bối cảnh. Tóm lại, một phương thức có tham số kiểu A
yêu cầu tham số ngầm định kiểu M[A]
:
def foo[A](implicit ma: M[A])
có thể được viết lại thành:
def foo[A: M]
Nhưng ý nghĩa của việc truyền tham số ngầm nhưng không đặt tên cho nó là gì? Làm thế nào điều này có thể hữu ích khi thực hiện phương pháp foo
?
Thông thường, tham số ngầm không cần phải được tham chiếu trực tiếp, nó sẽ được chuyển qua như là một đối số ngầm cho một phương thức khác được gọi. Nếu cần thiết, bạn vẫn có thể giữ lại chữ ký phương thức ngắn gọn với Giới hạn ngữ cảnh và gọi implicitly
để cụ thể hóa giá trị:
def foo[A: M] = {
val ma = implicitly[M[A]]
}
Truyền một tập hợp con các tham số ngầm
Giả sử bạn đang gọi một phương thức in đẹp một người, sử dụng cách tiếp cận dựa trên lớp loại:
trait Show[T] { def show(t: T): String }
object Show {
implicit def IntShow: Show[Int] = new Show[Int] { def show(i: Int) = i.toString }
implicit def StringShow: Show[String] = new Show[String] { def show(s: String) = s }
def ShoutyStringShow: Show[String] = new Show[String] { def show(s: String) = s.toUpperCase }
}
case class Person(name: String, age: Int)
object Person {
implicit def PersonShow(implicit si: Show[Int], ss: Show[String]): Show[Person] = new Show[Person] {
def show(p: Person) = "Person(name=" + ss.show(p.name) + ", age=" + si.show(p.age) + ")"
}
}
val p = Person("bob", 25)
implicitly[Show[Person]].show(p)
Điều gì xảy ra nếu chúng ta muốn thay đổi cách mà tên là đầu ra? Chúng tôi có thể gọi một cách rõ ràng PersonShow
, vượt qua một cách rõ ràng Show[String]
, nhưng chúng tôi muốn trình biên dịch vượt qua Show[Int]
.
Person.PersonShow(si = implicitly, ss = Show.ShoutyStringShow).show(p)