2013-09-01 20 views
5

Tôi đang chơi xung quanh với các bộ sưu tập trong Scala và thấy rằng các bộ sưu tập có thể thay đổi được định nghĩa là các bộ sưu tập bất biến và bất biến được định nghĩa là biến thể. Mối quan hệ giữa phương sai và khả năng biến đổi/bất biến trong Scala là gì?mối quan hệ giữa phương sai và biến đổi/bất biến trong Scala

class Array[T] 

class List[+T] 
+0

Đó chính xác là mối quan hệ. Bất biến là sự biến đổi, có thể biến đổi là bất biến. –

+0

Thực tế là, cấu trúc có thể thay đổi "gặp rắc rối" khi bạn để chúng trở thành biến thể. Vì lý do đó, Scala giới hạn các vị trí mà bạn có thể đặt các tham số kiểu biến đổi. Hãy xem tại đây: http://www.artima.com/pins1ed/type-parameterization.html Nếu bạn có thể, hãy thử tìm cùng một chương trong ấn bản thứ hai của cuốn sách. – Felix

Trả lời

4

Loại chỉ có thể được đánh dấu tương đối nếu thông số loại đó chỉ xuất hiện ở vị trí biến đổi. Nói chung, điều này có nghĩa là lớp/đặc điểm/đối tượng có các phương thức trả về các giá trị của kiểu biến thể nhưng không có phương thức với các tham số của kiểu biến thể. Bộ sưu tập có thể thay đổi luôn có các phương thức có tham số của loại biến thể, ví dụ: update. Hãy tưởng tượng điều gì sẽ xảy ra nếu Array có thể được khai báo Array [+ T]:

val as = Array[String]("a string") 

// this statement won't typecheck in actual Scala 
val aa: Array[AnyRef] = as 

aa(0) = ("I'm a string...", "but this tuple itself isn't!") 

// Tuples don't have a substring method, so this would fail at run-time 
// if the compiler allowed this code to compile. 
as(0).substring(0) 
5

Tôi đã tìm thấy một giải thích dễ dàng trong SIA. Sau đây là thẳng từ đó.

Đối tượng có thể biến đổi cần phải bất biến Thông số kiểu là bất biến khi không phải là biến thể cũng không phải là biến thể. Tất cả các lớp sưu tập có thể thay đổi Scala là bất biến. Một ví dụ có thể giải thích tại sao các đối tượng có thể biến đổi cần phải là bất biến. Bởi vì ListBuffer có thể thay đổi được, nó được khai báo là bất biến như sau:

final class ListBuffer[A] ...{ ... } 

Bởi vì nó được khai báo là bất biến, bạn không thể gán ListBuffer từ kiểu này sang kiểu khác. Mã sau đây sẽ ném một lỗi biên dịch:

scala> val mxs: ListBuffer[String] = ListBuffer("pants") 
mxs: scala.collection.mutable.ListBuffer[String] = 
      ListBuffer(pants) 
scala> val everything: ListBuffer[Any] = mxs 
<console>:6: error: type mismatch; 
found : scala.collection.mutable.ListBuffer[String] 
required: scala.collection.mutable.ListBuffer[Any] 
    val everything: ListBuffer[Any] = mxs 

Mặc dù String là một loại phụ của scala. Bất kỳ, Scala vẫn không cho phép bạn gán mx cho mọi thứ. Để hiểu lý do tại sao, giả sử ListBuffer là biến thể và đoạn mã sau hoạt động mà không có bất kỳ sự cố biên dịch nào:

scala> val mxs: ListBuffer[String] = ListBuffer("pants") 
    mxs: scala.collection.mutable.ListBuffer[String] = 
     ListBuffer(pants) 
    scala> val everything: ListBuffer[Any] = mxs 
    scala> everything += 1 
    res4: everything.type = ListBuffer(1, pants) 

Bạn có thể phát hiện sự cố không? Bởi vì mọi thứ đều thuộc kiểu Any, bạn có thể lưu trữ một giá trị số nguyên thành một tập hợp các chuỗi. Đây là một thảm họa đang chờ xảy ra. Đó chính là điều xảy ra với các mảng Java. Để tránh các loại vấn đề này, bạn nên tạo các đối tượng có thể thay đổi bất biến. Câu hỏi tiếp theo là những gì xảy ra trong trường hợp một đối tượng bất biến cho các bộ sưu tập. Hóa ra là đối với những vật thể bất biến, hiệp phương sai không phải là vấn đề gì cả. Nếu bạn thay thế ListBuffer bằng Danh sách bất biến, bạn có thể lấy một thể hiện của List [String] và gán nó vào List [Any] với một vấn đề.

scala> val xs: List[String] = List("pants") 
xs: List[String] = List(pants) 
scala> val everything: List[Any] = xs 
everything: List[Any] = List(pants) 

Lý do duy nhất bài tập này an toàn là vì Danh sách không thay đổi. Bạn có thể thêm 1 vào danh sách xs, và nó sẽ trả về một danh sách mới kiểu bất kỳ.

scala> 1 :: xs 
res5: List[Any] = List(1, pants) 

Một lần nữa, bổ sung này an toàn vì phương thức cons (:) luôn trả về một Danh sách mới và loại của nó được xác định theo loại yếu tố trong Danh sách. Loại duy nhất có thể lưu trữ một giá trị số nguyên và giá trị tham chiếu là scala.Any. Đây là thuộc tính quan trọng cần nhớ về phương sai kiểu khi xử lý các đối tượng có thể thay đổi/không thay đổi được.

Cách tốt nhất để hiểu contravariance là xem vấn đề xuất hiện khi vắng mặt. Cố gắng phát hiện sự cố trong ví dụ về mã Java sau:

Object[] arr = new int[1]; 
arr[0] = "Hello, there!"; 

Bạn sẽ chỉ định chuỗi đó vào một mảng nguyên. Java bắt lỗi này trong thời gian chạy bằng cách ném một ArrayStoreException.Scala dừng các loại lỗi này tại thời gian biên dịch bằng cách buộc các kiểu tham số trở thành contravariant hoặc bất biến.

Hy vọng điều này sẽ hữu ích.

+0

Đây là câu trả lời rất hay. –

Các vấn đề liên quan