Banyak pemula dan tidak begitu pengembang Scala menganggapnya sebagai fitur yang cukup berguna. Penggunaan biasanya terbatas lewat ExecutionContext
untuk Future
. Yang lain menghindari yang tersirat dan memandang peluang itu berbahaya.
Kode seperti ini membuat takut banyak orang:
implicit def function(implicit argument: A): B
Tapi saya pikir mekanisme ini merupakan keunggulan penting dari bahasa, mari kita lihat alasannya.
Secara singkat tentang implikasinya
Secara umum, implikasinya adalah mekanisme penyelesaian kode otomatis selama kompilasi:
untuk argumen implisit, nilainya secara otomatis diganti
untuk konversi implisit, nilainya secara otomatis digabungkan dalam panggilan metode
Saya tidak akan mendalami topik ini, yang tertarik untuk menonton video dari pembuat bahasa ini . Singkatnya, mekanisme yang satu ini digunakan dalam beberapa kasus berbeda (dan di Scala 3 akan dipecah menjadi beberapa fitur terpisah):
Meneruskan konteks implisit (seperti
ExecutionContext
)
Konversi implisit (tidak digunakan lagi)
Metode penyuluhan (gula sintaksis untuk menambahkan metode ke jenis yang ada)
Ketik kelas dan resolusi implisit
Ketik kelas dan inferensi implisit
Dengan sendirinya, kelas tipe tidak membawa kebaruan atau revolusi - mereka hanyalah implementasi metode "untuk" tipe, dan bukan "dalam" tipe itu sendiri, ini seperti perbedaan antara Comparable
& Ordering
( Comparator
di Java):
Comparable
:
class Person(age: Int) extends Comparable[Person] {
override def compareTo(o: Person): Int = age compareTo o.age
}
def max[T <: Comparable[T]](xs: Iterable[T]): T = xs.reduce[T] {
case (a, b) if (a compareTo b) < 0 => b
case (a, _) => a
}
Ordering
, :
case class Person(age: Int)
implicit object ByAgeOrdering extends Ordering[Person] {
override def compare(o1: Person, o2: Person): Int = o1.age compareTo o2.age
}
def max[T: Ordering](xs: Iterable[T]): T = xs.reduce[T] {
case (a, b) if Ordering[T].lt(a, b) => b
case (a, _) => a
}
// is syntactic sugar for
def max[T](xs: Iterable[T])(implicit evidence: Ordering[T]): T = ...
.
. :
implicit val value: A = ???
implicit def definition: B = ???
implicit def conversion(argument: C): D = ???
implicit def function(implicit argument: E): F = ???
? conversion
, , : value
, definition
& function
. : val
, def
. – .
– , , .
– , – , :
implicit def pairOrder[A: Ordering, B: Ordering]: Ordering[(A, B)] = {
case ((a1, b1), (a2, b2)) if Ordering[A].equiv(a1, a2) => Ordering[B].compare(b1, b2)
case ((a1, _), (a2, _)) => Ordering[A].compare(a1, a2)
}
// again, just syntactic sugar for:
implicit def pairOrder[A, B](implicit a: Ordering[A], b: Ordering[B]): Ordering[(A, B)] = ...
, :
val values = Seq(
(Person(30), ("A", "A")),
(Person(30), ("A", "B")),
(Person(20), ("A", "C"))
)
max(values) // => (Person(30),(A,B))
Seq[(Person, (String, String))]
Ordering
:
max(values)(
pairOrder(
ByAgeOrdering,
pairOrder(Ordering.String, Ordering.String)
)
)
Jadi inferensi implisit memungkinkan Anda mendeskripsikan aturan inferensi umum dan menginstruksikan compiler untuk menggabungkan aturan ini bersama-sama dan mendapatkan implementasi spesifik dari kelas tipe. Menambahkan tipe Anda sendiri atau aturan Anda sendiri tidak perlu menjelaskan semuanya dari awal - kompilator akan menggabungkan semuanya itu sendiri untuk mendapatkan objek yang diinginkan.
Dan yang paling penting, jika kompilator gagal, Anda akan menerima kesalahan kompilasi, bukan kesalahan runtime, dan Anda akan dapat segera memperbaiki masalah tersebut. Meskipun, tentu saja, ada lalat di salep di salep - jika kompiler gagal, Anda tidak tahu link mana yang hilang - tidak selalu mudah untuk men-debug ini.
Semoga implisit sekarang sedikit lebih eksplisit.