Scala - получение объекта класса из универсального типа - PullRequest
19 голосов
/ 21 ноября 2011

Можно ли создать объект Class исключительно из универсального параметра?Например:

class myclass[T] { 
  def something(): Class[_ <: T] = 
    classOf[T] //this doesn't work
}

Поскольку тип будет удален во время выполнения, кажется, что это работа для манифестов, но я не нашел пример, демонстрирующий это конкретное использование.Я попробовал следующее, но оно тоже не работает:

class myclass[T] { 
  def something()(implicit m: Manifest[T]): Class[_ <: T] = 
    m.erasure //this doesn't work
}

Я подозреваю, что эта ошибка связана с тем, как указывает API, между типами m.erasure нет отношения подтипа.результат и T.

РЕДАКТИРОВАТЬ: Меня не очень интересует тип T, мне просто нужен объект типа Class[_ <: T] для передачи в метод в рамках Hadoop.

Есть указатели?

Ответы [ 3 ]

29 голосов
/ 18 октября 2013
def myClassOf[T:ClassTag] = implicitly[ClassTag[T]].runtimeClass
14 голосов
/ 21 ноября 2011

Вы можете привести результат m.erasure к Class[T]:

class myclass[T] { 
    def something()(implicit m: Manifest[T]): Class[T] = 
        m.erasure.asInstanceOf[Class[T]]
}

Это прекрасно работает для базовых (не универсальных) типов:

scala> new myclass[String]().something()
res5: Class[String] = class java.lang.String

Но обратите вниманиечто произойдет, если я использую созданный экземпляр конструктора типа, например List[String] для T:

scala> new myclass[List[String]]().something()
res6: Class[List[String]] = class scala.collection.immutable.List

Из-за стирания существует только один объект Class для всех возможных реализаций данного конструктора типа.

Редактировать

Я не уверен, почему Manifest[T].erasure возвращает Class[_] вместо Class[T], но если бы мне пришлось размышлять, я бы сказал, что это отговорит вас от использования методовна Class, которые позволяют сравнивать два класса на равенство или отношение подтипа, поскольку эти методы будут давать неправильные ответы, когда Class параметризован с помощью экземпляра универсального типа.

Например,

scala> classOf[List[String]] == classOf[List[Int]]
res25: Boolean = true

scala> classOf[List[String]].isAssignableFrom(classOf[List[Int]])
res26: Boolean = true

Эти результаты могут вас удивить и / или привести к ошибке в вашей программе.Вместо того, чтобы сравнивать классы таким образом, вы должны просто передать вместо Manifest s и сравнить их, так как они имеют больше информации *:

scala> manifest[List[String]] == manifest[List[Int]]
res27: Boolean = false

scala> manifest[List[String]] >:> manifest[List[Int]]
res28: Boolean = false

Как я понимаю, Manifest s предназначены длязаменяет Class es для большинства случаев использования ... но, конечно, если вы используете фреймворк, требующий Class, выбора не так много.Я полагаю, что наложение результата erasure является всего лишь «признанием ответственности», что вы используете некачественный продукт на свой страх и риск:)

* Обратите внимание, что в качестве документация для Manifest говорит, что эти операторы сравнения манифестов "должны рассматриваться только как приближения, так как существуют многочисленные аспекты соответствия типов, которые еще недостаточно представлены в манифестах".

5 голосов
/ 21 ноября 2011

.erasure дает вам тип, до которого стирается ваш тип. Если вы хотите получить полную информацию о типе, вы должны вернуть Manifest.

scala> class MyClass[A] {
     |   def stuff(implicit m: Manifest[A]): Class[_] = m.erasure
     | }
defined class MyClass

scala> new MyClass[Int].stuff
res551: java.lang.Class[_] = int

scala> new MyClass[List[Int]].stuff
res552: java.lang.Class[_] = class scala.collection.immutable.List

scala> class MyClass[A] {
     |   def stuff(implicit m: Manifest[A]): Manifest[A] = m
     | }
defined class MyClass

scala> new MyClass[Int].stuff
res553: Manifest[Int] = Int

scala> new MyClass[List[Int]].stuff
res554: Manifest[List[Int]] = scala.collection.immutable.List[Int]
...