Về mặt tổng quát, một tham số loại covariant là một tham số được phép thay đổi khi lớp được phân loại (thay vào đó, thay đổi theo phân nhóm, do đó tiền tố "co-"). Cụ thể hơn:
trait List[+A]
List[Int]
là một kiểu con của List[AnyVal]
vì Int
là một kiểu con của AnyVal
. Điều này có nghĩa là bạn có thể cung cấp một ví dụ về List[Int]
thời điểm giá trị của loại List[AnyVal]
được mong đợi. Đây thực sự là một cách rất trực quan để thuốc generic hoạt động, nhưng hóa ra nó không có cơ sở (phá vỡ hệ thống loại) khi được sử dụng với sự có mặt của dữ liệu có thể thay đổi. Đây là lý do tại sao generic là bất biến trong Java. Ví dụ ngắn gọn về sự không chắc chắn bằng cách sử dụng các mảng Java (là sai số đồng biến):
Object[] arr = new Integer[1];
arr[0] = "Hello, there!";
Chúng tôi chỉ gán một giá trị của kiểu String
cho một mảng kiểu Integer[]
. Vì những lý do rõ ràng, đây là tin xấu. Hệ thống kiểu của Java thực sự cho phép điều này vào thời gian biên dịch. JVM sẽ "giúp đỡ" một cách hữu ích ArrayStoreException
khi chạy. Hệ thống loại của Scala ngăn chặn vấn đề này bởi vì tham số loại trên Array
lớp là bất biến (khai báo [A]
chứ không phải là [+A]
).
Lưu ý rằng có một loại phương sai gọi là contravariance . Điều này rất quan trọng vì nó giải thích tại sao hiệp phương sai có thể gây ra một số vấn đề. Chống chỉ định theo nghĩa đen là đối lập với hiệp phương sai: các tham số thay đổi theo hướng phụ với phân nhóm. Nó ít phổ biến hơn một phần vì nó rất phản trực giác, mặc dù nó có một ứng dụng rất quan trọng: chức năng.
trait Function1[-P, +R] {
def apply(p: P): R
}
Lưu ý chú thích phương sai " - " trên P
tham số loại. Tuyên bố này là toàn bộ một phương tiện mà Function1
là contravariant trong P
và hiệp biến trong R
. Vì vậy, chúng ta có thể rút ra các tiên đề sau:
T1' <: T1
T2 <: T2'
---------------------------------------- S-Fun
Function1[T1, T2] <: Function1[T1', T2']
Lưu ý rằng T1'
phải là một kiểu con (hoặc cùng loại) T1
, trong khi nó ngược lại cho T2
và T2'
. Trong tiếng Anh, điều này có thể được đọc như sau:
Một chức năng Một là một subtype của một chức năng B nếu kiểu tham số của A là một siêu kiểu của các loại tham số của B trong khi kiểu trả về của A là một subtype của kiểu trả về của B .
Lý do cho quy tắc này được để lại như một bài tập cho người đọc (gợi ý: suy nghĩ về các trường hợp khác nhau khi các hàm được phân loại, như ví dụ mảng của tôi ở trên).
Với kiến thức mới được tìm thấy về đồng và chống chỉ định, bạn sẽ có thể thấy lý do tại sao ví dụ sau sẽ không biên dịch:
trait List[+A] {
def cons(hd: A): List[A]
}
Vấn đề là A
covariant, trong khi cons
hàm dự kiến tham số kiểu của nó là bất biến . Như vậy, A
là thay đổi hướng sai. Điều thú vị là đủ, chúng ta có thể giải quyết vấn đề này bằng cách làm List
contravariant trong A
, nhưng sau đó các kiểu trả về List[A]
sẽ là không hợp lệ như các cons
chức năng hy vọng kiểu trả về của nó sẽ được hiệp biến .
Hai tùy chọn duy nhất của chúng tôi ở đây là a) tạo A
bất biến, mất các thuộc tính gõ phụ trực quan, đẹp mắt của hiệp phương sai hoặc b) thêm một tham số kiểu cục bộ vào cons
phương thức xác định A
là giới hạn dưới:
def cons[B >: A](v: B): List[B]
Điều này là hợp lệ. Bạn có thể tưởng tượng rằng A
đang thay đổi đi xuống, nhưng B
có thể thay đổi trở lên đối với các A
từ A
là nó thấp-bound. Với khai báo phương thức này, chúng ta có thể có A
covariant và mọi thứ đều ổn.
Lưu ý rằng thủ thuật này chỉ hoạt động nếu chúng ta trả về một thể List
hiện chuyên về loại ít cụ thể hơn B
. Nếu bạn cố gắng làm cho List
có thể thay đổi, mọi thứ sẽ bị hỏng do cuối cùng bạn cố gắng gán các giá trị của kiểu B
cho một biến kiểu A
, không được trình biên dịch cho phép. Bất cứ khi nào bạn có khả năng biến đổi, bạn cần phải có một trình biến đổi nào đó, yêu cầu một tham số phương thức của một loại nhất định, (cùng với trình truy cập) ngụ ý bất biến. Hiệp phương sai hoạt động với dữ liệu bất biến vì hoạt động khả dĩ duy nhất là một bộ truy cập, có thể được cung cấp một kiểu trả về hiệp phương sai.
var
có thể ổn định trong khival
không. Đó là cùng một lý do tại sao các bộ sưu tập bất biến của scala là đồng biến nhưng những bộ sưu tập có thể thay đổi thì không.