Chúng được gọi là các ràng buộc kiểu tổng quát . Chúng cho phép bạn, từ bên trong một lớp hoặc đặc tính tham số loại, tiếp tục ràng buộc một trong các tham số loại của nó. Đây là một ví dụ:
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
}
Đối số ngầm evidence
được cung cấp bởi trình biên dịch, iff A
là String
. Bạn có thể nghĩ về nó như một bằng chứng - đó A
là String
lý lẽ không quan trọng, chỉ biết rằng nó tồn tại. [chỉnh sửa: tốt, về mặt kỹ thuật nó thực sự quan trọng bởi vì nó đại diện cho một chuyển đổi ngầm từ A
sang String
, đó là những gì cho phép bạn gọi a.length
và không có trình biên dịch la mắng bạn]
Bây giờ tôi có thể sử dụng nó như vậy:
scala> Foo("blah").getStringLength
res6: Int = 4
Nhưng nếu tôi đã thử sử dụng nó với một Foo
thứ gì đó không phải là String
:
scala> Foo(123).getStringLength
<console>:9: error: could not find implicit value for parameter evidence: =:=[Int,String]
Bạn có thể đọc lỗi đó là "không thể tìm thấy bằng chứng cho thấy Int == String" ... đúng như vậy! getStringLength
đang áp đặt các hạn chế hơn nữa đối với loại A
hơn những gì Foo
nói chung yêu cầu; cụ thể là, bạn chỉ có thể gọi getStringLength
trên a Foo[String]
. Ràng buộc này được thi hành vào thời gian biên dịch, thật tuyệt!
<:<
và <%<
hoạt động tương tự, nhưng với các biến thể nhẹ:
A =:= B
có nghĩa là A phải chính xác B
A <:< B
có nghĩa là A phải là một kiểu con của B (tương tự như ràng buộc kiểu đơn giản<:
)
A <%< B
có nghĩa là A phải có thể xem được như B, có thể thông qua chuyển đổi ngầm định (tương tự như ràng buộc kiểu đơn giản <%
)
Đoạn trích này của @retronym là một lời giải thích tốt về cách thức loại điều này từng được thực hiện và cách các ràng buộc kiểu tổng quát làm cho nó dễ dàng hơn bây giờ.
ĐỊA CHỈ
Để trả lời câu hỏi tiếp theo của bạn, phải thừa nhận ví dụ tôi đưa ra khá giả tạo và rõ ràng không hữu ích. Nhưng hãy tưởng tượng sử dụng nó để định nghĩa một cái gì đó giống như một List.sumInts
phương thức, trong đó thêm một danh sách các số nguyên. Bạn không muốn cho phép phương thức này được gọi trên bất kỳ cái cũ nào List
, chỉ là a List[Int]
. Tuy nhiên, hàm tạo List
kiểu không thể bị hạn chế; bạn vẫn muốn có thể có danh sách các chuỗi, foos, bar và whatnots. Vì vậy, bằng cách đặt một ràng buộc kiểu tổng quát trên sumInts
, bạn có thể đảm bảo rằng chỉ phương thức đó có một ràng buộc bổ sung mà nó chỉ có thể được sử dụng trên a List[Int]
. Về cơ bản, bạn đang viết mã trường hợp đặc biệt cho một số loại danh sách nhất định.