2011-01-31 26 views
11

Xác định đoạn mã sau:Tại sao ClassManifest cần thiết với Array nhưng không phải Danh sách?

import scala.collection.JavaConversions._ 
val iter:java.util.Iterator[Any] = Array[Any](1, 2, 3).iterator 
def func(a:Any):String = a.toString 

def test[T:ClassManifest](iter:java.util.Iterator[Any], func:Any=>T):Array[T] = 
    iter.map(i=>func(i)).toArray 

def testFunc = test(iter, func) 

Ở đây, tôi cần phải sử dụng ClassManifest cho nó để biên dịch một cách chính xác, nếu không tôi nhận được lỗi:

scala> def test[T](iter:java.util.Iterator[Any], func:Any=>T):Array[T] = 
    | iter.map(i=>func(i)).toArray   

<console>:11: error: could not find implicit value for evidence parameter of 
type ClassManifest[T] 
    iter.map(i=>func(i)).toArray 
         ^

Mặt khác, mã thay thế bên dưới sử dụng List không yêu cầu điều này và biên dịch tốt.

import scala.collection.JavaConversions._ 
val iter:java.util.Iterator[Any] = Array[Any](1, 2, 3).iterator 
def func(a:Any):String = a.toString 

def test1[T](iter:java.util.Iterator[Any], func:Any=>T):List[T] = 
    iter.map(i=>func(i)).toList 


def testFunc1 = test1(iter, func).toArray 

Lưu ý rằng kết quả cuối cùng là testFunctestFunc1 giống nhau.

Làm cách nào để phiên bản List không yêu cầu ClassManifest?

Trả lời

10

Mảng trong Java không bị xóa, và đặc biệt, Array[Int] khác với Array[Object] ở cấp JVM.

Đối với bất kỳ lớp nào khác, các thông số loại sẽ bị xóa thành Object, vì vậy List[Int]List[Object] có cùng biểu diễn ở cấp JVM.

+0

Câu trả lời của bạn và Angel kết hợp với nhau tạo thành câu trả lời đúng :) – Jus12

2

Câu trả lời ngắn gọn là vì đó là cách các phương pháp là defined in the API:

def toArray [B >: A] (implicit arg0: ClassManifest[B]) : Array[B] 
def toList : List[A] 

Nếu bạn rời khỏi :ClassManifest trong def test[T:ClassManifest] trong mã của bạn, sau đó tất cả các trình biên dịch biết là nó có một số loại không rõ T và do đó trình biên dịch không có cách nào để tìm kiếm số ClassManifest cho loại đó.

Tại sao mã cần ClassManifest? Nếu bạn look at the source for toArray bạn sẽ thấy:

val result = new Array[B](size) 

constructor này Array đòi hỏi một ClassManifest. Xem câu trả lời của Easy Angel cho tài liệu này. Dưới đây là một ví dụ chứng minh nó trong REPL:

scala> def createArray[T] = new Array[T](10) 
<console>:5: error: cannot find class manifest for element type T 
     def createArray[T] = new Array[T](10) 

Vì vậy, về cơ bản, bạn phải viết T: ClassManifest vì Scala cần một ClassManifest để tạo ra một mảng mới.

10

Phương pháp toArray tạo mảng mới với các thành phần thuộc loại T. Và according to documentation:

So depending on the actual type parameter for T, this could be an Array[Int], or an Array[Boolean], or an array of some of the other primitive types in Java, or an array of some reference type. But these types have all different runtime representations, so how is the Scala runtime going to pick the correct one? In fact, it can't do that based on the information it is given, because the actual type that corresponds to the type parameter T is erased at runtime.

2

Scala sử dụng JVM mảng bản địa như thực hiện cho Array. Như vậy chỉ có thể được tạo ra với loại đã biết.

Kể từ khi Sun quyết định tạo các tạo tác cũ, các loại chung được xóa và không còn tồn tại trong mã byte nữa. Điều đó có nghĩa là khi chạy, Array[T] không còn biết giá trị của T và do đó, VM không thể tạo mảng. Có một số cạm bẫy phức tạp hơn bạn nhận được khi bạn cố gắng tương thích với Java 1.4 và giới thiệu các generics trên mảng (tôi cũng không nhận được toàn bộ chiều sâu), nhưng dòng dưới cùng là: các mảng của JVM/Java không phải là chung; Scala giúp chúng ta với sự trừu tượng chung chung.

Tệp kê khai lớp mà bạn đề cập đến, về cơ bản, là bản hack. Nó đảm bảo tĩnh rằng thông tin kiểu có sẵn khi một mảng được tạo ra bằng cách yêu cầu tham số ngầm định này. Trong thực tế, bất kỳ phương thức/hàm nào tạo ra một mảng của một tham số kiểu phải yêu cầu một tệp kê khai ngầm định, và tất cả các callees của nó và vân vân lên đến điểm là kiểu được biết (tĩnh).

+0

"có lẽ vì các đối tượng thực sự được đặt trên heap liên tiếp" Không, chúng không phải. Mảng của các đối tượng lưu trữ các tham chiếu tuần tự, không phải là các đối tượng. (Điều này không áp dụng cho các mảng nguyên thủy, tất nhiên.) –

+1

Chỉnh sửa có lợi cho một lý do mơ hồ hơn. Vít di sản, nghiêm túc ... – Raphael

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