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 m
pada objek o
kelas C
, dan kelas itu tidak mendukung metode m
, maka Scala akan mencari konversi implisit dari C
ke sesuatu yang memang mendukung m
. Contoh sederhana akan menjadi metode map
pada String
:
"abc".map(_.toInt)
String
tidak mendukung metode ini map
, tetapi mendukung StringOps
, dan ada konversi tersirat dari String
menjadi StringOps
tersedia (lihat implicit def augmentString
di 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 foo
deklarasi 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 getIndex
dapat menerima objek apa pun, asalkan ada konversi implisit yang tersedia dari kelasnya ke Seq[T]
. Karena itu, saya dapat meneruskan String
ke 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, getIndex
dapat 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 Integral
kelas 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 sorted
pada Seq
perlu 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 Option
ada konversi implisit ke Iterable
, sehingga orang dapat memanggil Iterable
metode Option
, atau meneruskan Option
ke 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.flatMap
mengharapkan TraversableOnce
, yang Option
tidak. Kompilator kemudian melihat ke dalam Option
objek 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 sorted
mengambil 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 A
juga akan dipertimbangkan. Dengan "ruang lingkup implisit" Maksud saya bahwa semua aturan ini akan diterapkan secara berulang - misalnya, objek pendamping dari A
akan dicari implisit, seperti aturan di atas.
Perhatikan bahwa ini tidak berarti ruang lingkup implisit dari A
akan 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 Ordering
untuk 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 sorted
mengharapkan 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 CanBuildFrom
pekerjaan: implisit ditemukan di dalam objek pengiring ke parameter tipe CanBuildFrom
.
Catatan : Ordering
didefinisikan sebagai trait Ordering[T]
, di mana T
parameter tipe. Sebelumnya, saya mengatakan bahwa Scala melihat ke dalam parameter tipe, yang tidak masuk akal. Implisit mencari di atas adalah Ordering[A]
, di mana A
merupakan 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: