Jenis-Jenis Implikasinya
Implicits dalam Scala mengacu pada nilai yang dapat dilewati "secara otomatis", sehingga, atau konversi dari satu jenis ke yang lain yang dibuat secara otomatis.
Konversi Tersirat
Berbicara sangat singkat tentang tipe yang terakhir, jika seseorang memanggil metode mpada objek okelas C, dan kelas itu tidak mendukung metode m, maka Scala akan mencari konversi implisit dari Cke sesuatu yang memang mendukung m. Contoh sederhana akan menjadi metode mappada String:
"abc".map(_.toInt)
Stringtidak mendukung metode ini map, tetapi mendukung StringOps, dan ada konversi tersirat dari Stringmenjadi StringOpstersedia (lihat implicit def augmentStringdi Predef).
Parameter Tersirat
Jenis implisit lainnya adalah parameter implisit . Ini diteruskan ke pemanggilan metode seperti parameter lainnya, tetapi kompiler mencoba mengisinya secara otomatis. Jika tidak bisa, itu akan mengeluh. Seseorang dapat melewatkan parameter-parameter ini secara eksplisit, seperti itulah yang digunakan seseorang breakOut, misalnya (lihat pertanyaan tentang breakOut, pada hari Anda merasa tertantang).
Dalam hal ini, seseorang harus menyatakan perlunya implisit, seperti foodeklarasi metode:
def foo[T](t: T)(implicit integral: Integral[T]) {println(integral)}
Lihat Batas
Ada satu situasi di mana sebuah implisit adalah konversi implisit dan parameter implisit. Sebagai contoh:
def getIndex[T, CC](seq: CC, value: T)(implicit conv: CC => Seq[T]) = seq.indexOf(value)
getIndex("abc", 'a')
Metode ini getIndexdapat menerima objek apa pun, asalkan ada konversi implisit yang tersedia dari kelasnya ke Seq[T]. Karena itu, saya dapat meneruskan Stringke getIndex, dan itu akan berhasil.
Di belakang layar, kompiler berubah seq.IndexOf(value)menjadi conv(seq).indexOf(value).
Ini sangat berguna sehingga ada gula sintaksis untuk menuliskannya. Menggunakan gula sintaksis ini, getIndexdapat didefinisikan seperti ini:
def getIndex[T, CC <% Seq[T]](seq: CC, value: T) = seq.indexOf(value)
Gula sintaksis ini digambarkan sebagai pandangan terikat , mirip dengan batas atas ( CC <: Seq[Int]) atau batas bawah ( T >: Null).
Batas Konteks
Pola umum lainnya dalam parameter implisit adalah pola kelas tipe . Pola ini memungkinkan penyediaan antarmuka umum ke kelas yang tidak mendeklarasikannya. Keduanya dapat berfungsi sebagai pola jembatan - mendapatkan pemisahan kekhawatiran - dan sebagai pola adaptor.
The Integralkelas yang Anda sebutkan adalah contoh klasik dari pola jenis kelas. Contoh lain tentang perpustakaan standar Scala adalah Ordering. Ada perpustakaan yang banyak menggunakan pola ini, yang disebut Scalaz.
Ini adalah contoh penggunaannya:
def sum[T](list: List[T])(implicit integral: Integral[T]): T = {
import integral._ // get the implicits in question into scope
list.foldLeft(integral.zero)(_ + _)
}
Ada juga gula sintaksis untuk itu, yang disebut konteks terikat , yang dibuat kurang bermanfaat oleh kebutuhan untuk merujuk pada implisit. Konversi langsung dari metode itu terlihat seperti ini:
def sum[T : Integral](list: List[T]): T = {
val integral = implicitly[Integral[T]]
import integral._ // get the implicits in question into scope
list.foldLeft(integral.zero)(_ + _)
}
Batas konteks lebih berguna ketika Anda hanya perlu meneruskannya ke metode lain yang menggunakannya. Sebagai contoh, metode sortedpada Seqperlu implisit Ordering. Untuk membuat metode reverseSort, seseorang dapat menulis:
def reverseSort[T : Ordering](seq: Seq[T]) = seq.sorted.reverse
Karena Ordering[T]secara implisit diteruskan ke reverseSort, maka dapat diteruskan secara implisit kepada sorted.
Dari mana Implik berasal?
Ketika kompilator melihat perlunya implisit, baik karena Anda memanggil metode yang tidak ada pada kelas objek, atau karena Anda memanggil metode yang membutuhkan parameter implisit, ia akan mencari implisit yang sesuai dengan kebutuhan. .
Pencarian ini mematuhi aturan tertentu yang menentukan yang tersirat terlihat dan yang tidak. Tabel berikut menunjukkan di mana kompiler akan mencari implisit diambil dari presentasi yang sangat baik tentang implisit oleh Josh Suereth, yang saya sungguh-sungguh merekomendasikan kepada siapa pun yang ingin meningkatkan pengetahuan Scala mereka. Sejak itu telah dilengkapi dengan umpan balik dan pembaruan.
Implisit yang tersedia pada nomor 1 di bawah ini lebih diutamakan daripada yang di bawah angka 2. Selain itu, jika ada beberapa argumen yang memenuhi syarat yang sesuai dengan tipe parameter implisit, yang paling spesifik akan dipilih menggunakan aturan resolusi kelebihan beban statis (lihat Scala Spesifikasi §6.26.3). Informasi lebih rinci dapat ditemukan dalam pertanyaan yang saya tautkan pada akhir jawaban ini.
- Tampilan pertama dalam lingkup saat ini
- Implikasi didefinisikan dalam ruang lingkup saat ini
- Impor eksplisit
- impor wildcard
Cakupan yang sama di file lain
- Sekarang lihat tipe terkait di
- Objek pendamping dari suatu tipe
- Lingkup implisit dari tipe argumen (2.9.1)
- Lingkup argumen tipe implisit (2.8.0)
- Benda luar untuk tipe bersarang
- Dimensi lainnya
Mari kita berikan beberapa contoh untuk mereka:
Implikasi Didefinisikan dalam Lingkup Saat Ini
implicit val n: Int = 5
def add(x: Int)(implicit y: Int) = x + y
add(5) // takes n from the current scope
Impor Eksplisit
import scala.collection.JavaConversions.mapAsScalaMap
def env = System.getenv() // Java map
val term = env("TERM") // implicit conversion from Java Map to Scala Map
Impor Kartu Liar
def sum[T : Integral](list: List[T]): T = {
val integral = implicitly[Integral[T]]
import integral._ // get the implicits in question into scope
list.foldLeft(integral.zero)(_ + _)
}
Lingkup yang Sama di File Lain
Sunting : Sepertinya ini tidak memiliki prioritas yang berbeda. Jika Anda memiliki beberapa contoh yang menunjukkan perbedaan yang diutamakan, silakan berikan komentar. Kalau tidak, jangan mengandalkan yang ini.
Ini seperti contoh pertama, tetapi mengasumsikan definisi implisit dalam file yang berbeda dari penggunaannya. Lihat juga bagaimana objek paket dapat digunakan untuk memasukkan implisit.
Objek Pendamping Jenis
Ada dua objek sahabat catatan di sini. Pertama, objek pendamping dari jenis "sumber" diperiksa. Misalnya, di dalam objek Optionada konversi implisit ke Iterable, sehingga orang dapat memanggil Iterablemetode Option, atau meneruskan Optionke sesuatu yang mengharapkan suatu Iterable. Sebagai contoh:
for {
x <- List(1, 2, 3)
y <- Some('x')
} yield (x, y)
Ungkapan itu diterjemahkan oleh kompiler ke
List(1, 2, 3).flatMap(x => Some('x').map(y => (x, y)))
Namun, List.flatMapmengharapkan TraversableOnce, yang Optiontidak. Kompilator kemudian melihat ke dalam Optionobjek pendamping dan menemukan konversi ke Iterable, yang merupakan TraversableOnce, membuat ungkapan ini benar.
Kedua, objek pendamping dari tipe yang diharapkan:
List(1, 2, 3).sorted
Metode ini sortedmengambil implisit Ordering. Dalam hal ini, terlihat di dalam objek Ordering, pendamping kelas Ordering, dan menemukan implisit di Ordering[Int]sana.
Perhatikan bahwa objek pengiring dari kelas super juga diperiksa. Sebagai contoh:
class A(val n: Int)
object A {
implicit def str(a: A) = "A: %d" format a.n
}
class B(val x: Int, y: Int) extends A(y)
val b = new B(5, 2)
val s: String = b // s == "A: 2"
Beginilah Scala menemukan yang tersirat Numeric[Int]dan Numeric[Long]dalam pertanyaan Anda, omong-omong, seperti yang ditemukan di dalam Numeric, bukan Integral.
Lingkup Implisit dari Jenis Argumen
Jika Anda memiliki metode dengan tipe argumen A, maka cakupan tipe implisit Ajuga akan dipertimbangkan. Dengan "ruang lingkup implisit" Maksud saya bahwa semua aturan ini akan diterapkan secara berulang - misalnya, objek pendamping dari Aakan dicari implisit, seperti aturan di atas.
Perhatikan bahwa ini tidak berarti ruang lingkup implisit dari Aakan dicari konversi dari parameter itu, tetapi dari keseluruhan ekspresi. Sebagai contoh:
class A(val n: Int) {
def +(other: A) = new A(n + other.n)
}
object A {
implicit def fromInt(n: Int) = new A(n)
}
// This becomes possible:
1 + new A(1)
// because it is converted into this:
A.fromInt(1) + new A(1)
Ini tersedia sejak Scala 2.9.1.
Lingkup Argumen Jenis Tersirat
Ini diperlukan untuk membuat pola kelas tipe benar-benar berfungsi. Pertimbangkan Ordering, misalnya: Muncul dengan beberapa implisit dalam objek pendampingnya, tetapi Anda tidak dapat menambahkan barang ke sana. Jadi bagaimana Anda bisa membuat Orderinguntuk kelas Anda sendiri yang secara otomatis ditemukan?
Mari kita mulai dengan implementasinya:
class A(val n: Int)
object A {
implicit val ord = new Ordering[A] {
def compare(x: A, y: A) = implicitly[Ordering[Int]].compare(x.n, y.n)
}
}
Jadi, pertimbangkan apa yang terjadi ketika Anda menelepon
List(new A(5), new A(2)).sorted
Seperti yang kita lihat, metode sortedmengharapkan Ordering[A](sebenarnya, mengharapkan Ordering[B], di mana B >: A). Tidak ada hal seperti itu di dalam Ordering, dan tidak ada jenis "sumber" yang akan dilihat. Jelas, itu adalah menemukan di dalam A, yang merupakan jenis argumen dari Ordering.
Ini juga bagaimana berbagai metode pengumpulan mengharapkan CanBuildFrompekerjaan: implisit ditemukan di dalam objek pengiring ke parameter tipe CanBuildFrom.
Catatan : Orderingdidefinisikan sebagai trait Ordering[T], di mana Tparameter tipe. Sebelumnya, saya mengatakan bahwa Scala melihat ke dalam parameter tipe, yang tidak masuk akal. Implisit mencari di atas adalah Ordering[A], di mana Amerupakan tipe yang sebenarnya, bukan jenis parameter: itu adalah argumen tipe untuk Ordering. Lihat bagian 7.2 dari spesifikasi Scala.
Ini tersedia sejak Scala 2.8.0.
Objek Luar untuk Jenis Bertingkat
Saya belum benar-benar melihat contoh ini. Saya akan berterima kasih jika seseorang dapat membagikannya. Prinsipnya sederhana:
class A(val n: Int) {
class B(val m: Int) { require(m < n) }
}
object A {
implicit def bToString(b: A#B) = "B: %d" format b.m
}
val a = new A(5)
val b = new a.B(3)
val s: String = b // s == "B: 3"
Dimensi Lainnya
Saya cukup yakin ini hanya lelucon, tetapi jawaban ini mungkin tidak terkini. Jadi jangan anggap pertanyaan ini sebagai wasit terakhir dari apa yang terjadi, dan jika Anda tahu itu sudah ketinggalan zaman, mohon informasikan kepada saya sehingga saya dapat memperbaikinya.
EDIT
Pertanyaan terkait yang menarik: