[scala] Scala에서 유형 삭제를 어떻게 해결합니까? 또는 왜 내 컬렉션의 type 매개 변수를 얻을 수 없습니까?

스칼라에서 슬픈 사실은 List [Int]를 인스턴스화하면 인스턴스가 List인지 확인할 수 있고, 인스턴스의 개별 요소가 Int인지 확인할 수 있다는 것입니다. Int]로 쉽게 확인할 수 있습니다.

scala> List(1,2,3) match {
     | case l : List[String] => println("A list of strings?!")
     | case _ => println("Ok")
     | }
warning: there were unchecked warnings; re-run with -unchecked for details
A list of strings?!

-unchecked 옵션은 유형 삭제에 대해 책임을지게합니다.

scala>  List(1,2,3) match {
     |  case l : List[String] => println("A list of strings?!")
     |  case _ => println("Ok")
     |  }
<console>:6: warning: non variable type-argument String in type pattern is unchecked since it is eliminated by erasure
        case l : List[String] => println("A list of strings?!")
                 ^
A list of strings?!

그 이유는 무엇이며 어떻게 해결해야합니까?



답변

이 답변은 ManifestScala 2.10부터 더 이상 사용되지 않는 -API를 사용합니다 . 최신 솔루션에 대해서는 아래 답변을 참조하십시오.

Scala는 Java와 달리 JVM (Java Virtual Machine)이 제네릭을 얻지 못했기 때문에 Type Erasure로 정의되었습니다. 이는 런타임시 유형 매개 변수가 아닌 클래스 만 존재 함을 의미합니다. 이 예에서 JVM은가 처리하고 있음을 알고 scala.collection.immutable.List있지만이 목록이로 매개 변수화되어 있지는 않습니다 Int.

다행히도 스칼라에는이를 해결할 수있는 기능이 있습니다. 그것은이다 매니페스트 . 매니페스트는 인스턴스가 유형을 나타내는 객체 인 클래스입니다. 이러한 인스턴스는 객체이므로이를 전달하고 저장하며 일반적으로 메소드를 호출 할 수 있습니다. 암시 적 매개 변수를 지원하므로 매우 강력한 도구가됩니다. 예를 들어 다음 예를 보자.

object Registry {
  import scala.reflect.Manifest

  private var map= Map.empty[Any,(Manifest[_], Any)]

  def register[T](name: Any, item: T)(implicit m: Manifest[T]) {
    map = map.updated(name, m -> item)
  }

  def get[T](key:Any)(implicit m : Manifest[T]): Option[T] = {
    map get key flatMap {
      case (om, s) => if (om <:< m) Some(s.asInstanceOf[T]) else None
    }
  }
}

scala> Registry.register("a", List(1,2,3))

scala> Registry.get[List[Int]]("a")
res6: Option[List[Int]] = Some(List(1, 2, 3))

scala> Registry.get[List[String]]("a")
res7: Option[List[String]] = None

요소를 저장할 때 “Manifest”도 저장합니다. 매니페스트는 인스턴스가 스칼라 유형을 나타내는 클래스입니다. 이러한 객체에는 JVM보다 많은 정보가 있으므로 매개 변수화 된 전체 유형을 테스트 할 수 있습니다.

그러나 a Manifest는 여전히 진화하는 기능입니다. 한계의 예로서, 현재 분산에 대해 아무것도 모르고 모든 것이 공변량이라고 가정합니다. 현재 개발중인 Scala 리플렉션 라이브러리가 완성되면 더 안정적이고 견고해질 것으로 기대합니다.


답변

다니엘이 이미 언급했듯이 TypeTag를 사용 하여이 작업을 수행 할 수 있지만 명시 적으로 철자하겠습니다.

import scala.reflect.runtime.universe._
def matchList[A: TypeTag](list: List[A]) = list match {
  case strlist: List[String @unchecked] if typeOf[A] =:= typeOf[String] => println("A list of strings!")
  case intlist: List[Int @unchecked] if typeOf[A] =:= typeOf[Int] => println("A list of ints!")
}

ClassTag를 사용 하여이 작업을 수행 할 수도 있습니다 (스칼라 반사에 의존하지 않아도 됨).

import scala.reflect.{ClassTag, classTag}
def matchList2[A : ClassTag](list: List[A]) = list match {
  case strlist: List[String @unchecked] if classTag[A] == classTag[String] => println("A List of strings!")
  case intlist: List[Int @unchecked] if classTag[A] == classTag[Int] => println("A list of ints!")
}

type 매개 변수 A자체가 제네릭 형식 이 아닌 경우 ClassTag를 사용할 수 있습니다 .

불행히도 조금 장황하며 컴파일러 경고를 표시하지 않으려면 @unchecked 주석이 필요합니다. https://issues.scala-lang.org/browse/SI-6517 : 나중에 TypeTag가 컴파일러에서 자동으로 패턴 일치에 통합 될 수 있습니다


답변

shapeless 에서 Typeabletype 클래스를 사용하여 원하는 결과를 얻을 수 있습니다.

샘플 REPL 세션

scala> import shapeless.syntax.typeable._
import shapeless.syntax.typeable._

scala> val l1 : Any = List(1,2,3)
l1: Any = List(1, 2, 3)

scala> l1.cast[List[String]]
res0: Option[List[String]] = None

scala> l1.cast[List[Int]]
res1: Option[List[Int]] = Some(List(1, 2, 3))

사용 가능한 cast범위 내 Typeable인스턴스를 고려할 때 가능한 한 정밀한 삭제 작업 이 수행됩니다 .


답변

필자는 제한된 사용 상황에서 충분할 비교적 간단한 솔루션을 고안했습니다. 기본적으로 match 문에 사용할 수있는 래퍼 클래스의 유형 삭제 문제로 인해 발생하는 매개 변수화 된 유형을 래핑했습니다.

case class StringListHolder(list:List[String])

StringListHolder(List("str1","str2")) match {
    case holder: StringListHolder => holder.list foreach println
}

예상 출력이 있으며 케이스 클래스의 컨텐츠를 원하는 유형 인 문자열 목록으로 제한합니다.

자세한 내용은 여기 : http://www.scalafied.com/?p=60


답변

스칼라에는 유형 삭제 문제를 극복 할 수있는 방법이 있습니다. 에서 일치 하나의 극복 유형 소거일치의 극복 유형 소거 2 (분산)는 코드에 몇 가지 헬퍼 일치를 위해, 분산을 포함하여 유형을 포장하는 방법에 대한 몇 가지 설명이있다.


답변

나는 그렇지 않은 멋진 언어 의이 제한에 대해 약간 더 나은 해결 방법을 찾았습니다.

스칼라에서는 삭제 유형 문제가 배열에서 발생하지 않습니다. 예를 들어 이것을 설명하는 것이 더 쉽다고 생각합니다.

의 목록이 있다고 가정 (Int, String)하면 다음은 유형 삭제 경고를 제공합니다.

x match {
  case l:List[(Int, String)] =>
  ...
}

이를 해결하려면 먼저 케이스 클래스를 작성하십시오.

case class IntString(i:Int, s:String)

그런 다음 패턴 일치에서 다음과 같은 작업을 수행하십시오.

x match {
  case a:Array[IntString] =>
  ...
}

완벽하게 작동하는 것 같습니다.

목록 대신 배열을 사용하려면 코드를 약간 변경해야하지만 큰 문제는 아닙니다.

를 사용 case a:Array[(Int, String)]하면 여전히 유형 삭제 경고가 표시되므로 새 컨테이너 클래스 (이 예에서는 IntString) 를 사용해야합니다 .


답변

Java는 실제 요소 유형을 알지 못하기 때문에을 사용하는 것이 가장 유용하다는 것을 알았습니다 List[_]. 그런 다음 경고가 사라지고 코드는 현실을 설명합니다-알 수없는 것의 목록입니다.