2011-01-31 17 views
11

다음 코드 정의 : 한편왜 ClassManifest는 Array가 필요하지만 List는 필요하지 않습니까?

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 
         ^

사용하여 아래의 대체 코드 : 여기

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) 

을, 나는 그렇지 않으면 나는 오류, 제대로 컴파일하는 ClassManifest를 사용할 필요가 List이 필요하지 않고 잘 컴파일됩니다. testFunctestFunc1의 최종 출력이 동일하다는

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 

참고.

어떻게 List 버전은 ClassManifest이 필요하지 않습니까?

답변

10

Java의 배열은 유형이 지워지지 않으며, 특히 Array[Int]은 JVM 수준에서 Array[Object]과 다릅니다.

다른 클래스의 경우 유형 매개 변수가 Object으로 지워 지므로 List[Int]List[Object]은 JVM 수준에서 동일한 표현을 갖습니다.

+0

당신과 천사의 대답은 함께 올바른 답을 결합했습니다 :) – Jus12

2

짧은 대답은 그 때문에 방법이 defined in the API있는 방법은 다음 코드에서 def test[T:ClassManifest]:ClassManifest을두면

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

, 모든 컴파일러가 알고는 알 수없는 유형을 가지고 있다는 것입니다 T 때문에 컴파일러는 해당 유형에 대해 ClassManifest을 찾을 방법이 없습니다.

코드에 ClassManifest이 필요한 이유는 무엇입니까? 당신이 볼 수 look at the source for toArray 경우

val result = new Array[B](size) 

Array의이 생성자는 ClassManifest이 필요합니다. 자세한 내용은 Easy Angel의 대답을 참조하십시오. 여기서 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) 

그래서 기본적으로, 당신은 스칼라 새로운 배열을 생성하기 위해 ClassManifest이 필요하기 때문에 T: ClassManifest를 작성해야합니다.

10

방법 toArrayT 유형의 요소로 새 배열을 만듭니다. 그리고 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

스칼라 Array에 대한 구현으로 JVM 기본 배열을 사용합니다. 이러한 유형은 알려진 유형으로 만 만들 수 있습니다.

Sun은 레거시 아티팩트를 만들기로 결정한 이후 제네릭 형식이 지워지고 더 이상 바이트 코드로 표시되지 않습니다. 즉, 런타임에 Array[T]은 더 이상 T의 값을 알지 못하므로 VM은 배열을 만들 수 없습니다. Java 1.4와의 호환성을 시도하고 배열에 제네릭을 도입 할 때 좀 더 복잡한 함정이 있습니다 (전체 깊이를 얻지는 못하지만). 결론은 다음과 같습니다. JVM/Java의 배열은 일반적이지 않습니다. 스칼라는 일반적인 추상화에 도움이된다.

언급 한 클래스 매니페스트는 본질적으로 해킹입니다. 이 암시 적 매개 변수를 요구하여 배열을 만들 때 정적으로 형식 정보를 사용할 수 있습니다. 실제로 유형 매개 변수의 배열을 만드는 모든 메소드/함수는 암시 적 매니페스트를 요구해야하며 모든 호출 수신자 등은 유형이 알려진 (정적으로) 지점까지 호출됩니다.

+0

"아마도 실제로 개체가 실제로 힙에 배치되기 때문에"아니요, 그렇지 않습니다. 객체 배열은 객체 자체가 아닌 참조를 순차적으로 저장합니다. (물론 프리미티브 배열에는 적용되지 않습니다.) –

+1

더 모호한 추론을 위해 편집되었습니다. 레거시, 심각하게 스크류 ... – Raphael

관련 문제