Ini disebut batasan tipe umum . Mereka memungkinkan Anda, dari dalam kelas tipe-parameter atau sifat, untuk lebih jauh membatasi salah satu parameter jenisnya. Ini sebuah contoh:
case class Foo[A](a:A) { // 'A' can be substituted with any type
// getStringLength can only be used if this is a Foo[String]
def getStringLength(implicit evidence: A =:= String) = a.length
}
Argumen implisit evidence
disediakan oleh kompiler, iff A
adalah String
. Anda dapat menganggap itu sebagai bukti bahwa A
adalah String
argumen --the itu sendiri tidak penting, hanya mengetahui bahwa itu ada. [edit: baiklah, secara teknis itu sebenarnya penting karena itu merupakan konversi implisit dari A
ke String
, yang memungkinkan Anda menelepon a.length
dan tidak meminta kompiler berteriak kepada Anda]
Sekarang saya bisa menggunakannya seperti ini:
scala> Foo("blah").getStringLength
res6: Int = 4
Tetapi jika saya mencoba menggunakannya dengan Foo
sesuatu yang mengandung selain String
:
scala> Foo(123).getStringLength
<console>:9: error: could not find implicit value for parameter evidence: =:=[Int,String]
Anda dapat membaca kesalahan itu sebagai "tidak dapat menemukan bukti bahwa Int == String" ... itu sudah seharusnya! getStringLength
memaksakan pembatasan lebih lanjut pada jenis dari A
apa yang Foo
secara umum mengharuskan; yaitu, Anda hanya dapat meminta getStringLength
pada a Foo[String]
. Batasan ini diberlakukan pada waktu kompilasi, yang keren!
<:<
dan <%<
bekerja serupa, tetapi dengan sedikit variasi:
A =:= B
berarti A harus persis B
A <:< B
berarti A harus merupakan subtipe B (analog dengan batasan tipe sederhana<:
)
A <%< B
berarti A harus dapat dilihat sebagai B, mungkin melalui konversi implisit (analog dengan batasan tipe sederhana <%
)
Cuplikan oleh @retronym ini adalah penjelasan yang bagus tentang bagaimana hal seperti ini dapat diselesaikan dan bagaimana batasan tipe umum membuatnya lebih mudah sekarang.
TAMBAHAN
Untuk menjawab pertanyaan tindak lanjut Anda, diakui contoh yang saya berikan cukup dibuat-buat dan tidak jelas berguna. Tapi bayangkan menggunakannya untuk mendefinisikan sesuatu seperti List.sumInts
metode, yang menambahkan daftar bilangan bulat. Anda tidak ingin mengizinkan metode ini dipanggil pada yang lama List
, hanya a List[Int]
. Namun List
konstruktor tipe tidak dapat dibatasi; Anda masih ingin dapat memiliki daftar string, foo, bar, dan apa pun. Jadi dengan menempatkan batasan tipe umum sumInts
, Anda dapat memastikan bahwa hanya metode yang memiliki kendala tambahan yang hanya dapat digunakan pada a List[Int]
. Pada dasarnya Anda sedang menulis kode kasus khusus untuk beberapa jenis daftar.