Проблемы с манифестами / тегами типов при обновлении до Scala 2.10


Сижу перед проектом около 10000 LoC. Мне нужно обновить этот проект со Scala 2.9 до 2.10. Это было хорошо сделано, но я получил много предупреждений об устаревании из-за манифестов.

После использования функции поиска stackoverflow и многих других сайтов у меня не так много вопросов. Хочу подвести итог; ключевые моменты:

  1. TypeTags и ClassTags намного лучше, чем Manifests и ClassManifest. В частности, вы можете использовать их как синонимы (TypeTags ‹-> Manifests и ClassTags ‹-> ClassManifest)

  2. TypeTags более мощные, чем ClassTags, соответственно ClassTags более ограничены TypeTags. Первый вопрос: В этом проекте часто используется метод manifest[T].erasure.getSimpleName. Теперь я не могу переключить это только на typeTag[T].runtimeClass.getSimpleName, потому что код не будет компилироваться, но с classTag[T].runtimeClass.getSimpleName он будет компилироваться. Повлияет ли это на семантику? (Примечание. Метод erasure также устарел; вместо него следует использовать runtimeClass)

  3. Второй вопрос: Проверка типов манифестов в Scala 2.9 была вроде: manifest[T] <:< manifest[A]. В Scala 2.10 я бы написал это typeOf[T] <:< typeOf[A]. Но <:< устарел ?!

  4. Могу ли я преобразовать TypeTag в ClassTag? То есть: если я использую манифесты только для проверки типов (№ 3) и извлечения имен (№ 2): Могу ли я переименовать каждый Manifest / ClassManifest в ClassTag?


person funnyF    schedule 27.07.2013    source источник


Ответы (1)


  1. Да, в основном у вас есть эквивалент TypeTag <-> Manifest и ClassTag <-> ClassManifest. За исключением того, что есть некоторые вещи, которые раньше обрабатывались манифестами, которые не имеют прямого эквивалента, потому что вместо этого эти операции были перемещены глубже в API отражения, например, фабричные методы для объекта Manifest.

  2. ClassTag теперь в основном используется только для получения (стертого) класса времени выполнения чего-либо. Главное, для чего он используется, - это создание массива, но вы можете без проблем использовать его для других целей. Так что да, classTag[T].runtimeClass - это новый manifest[T].erasure и полностью ему эквивалентен.

  3. Это место, которое больше всего изменилось. 2.10 представил новый API отражения, и вы должны его использовать когда вы хотите ответить на конкретные вопросы о типах (например, «делает A <:< B?»). А точкой входа в API отражения для типа является ... его TypeTag.

    Если предположить, что теги типа для A и T входят в область видимости, новым manifest[T] <:< manifest[A] действительно будет typeOf[T] <:< typeOf[A]. Метод <:< здесь не считается устаревшим, см. скаляр. Однако есть устаревший <:< в ClassTag, потому что у ClassManifest он был раньше, а ClassTag - это новый ClassManifest.

    См. Раздел Общие операции с типами руководство по отражению и вопрос SO Что такое TypeTag и как его использовать.

  4. Я не думаю, что есть прямой способ перейти от TypeTag к ClassTag. Подумайте об этом так:

    • если вам нужен подтип или проверка равенства типов, вы не можете использовать ClassTag, вам нужен TypeTag;

    • если вам также нужно имя этого типа, вы можете просто получить его из TypeTag: typeOf[T].typeSymbol.name.decoded. Это даст вам стертое имя, как и имя класса, которое вы использовали для получения (List для List[Int] или Map для Map[String, Int]). Это немного отличается от фактического имени Class. Если вам нужно полное не стертое имя (List[Int]), достаточно typeOf[T].normalize.toString.

    • если вам также абсолютно необходим экземпляр Class или настоящее имя класса, которое вы можете передать и загрузить позже с отражением, Думаю, у вас нет другого выбора, кроме как попросить ClassTag тоже ..

      Изменить: я только что нашел этот SO вопрос, так что да, можно получить ClassTag от TypeTag. Просто грустно, что где-то в библиотеке отражений нет вспомогательного метода для этого.

      Изменить 2: в соответствии с запросом, вот как преобразовать из TypeTag в ClassTag:

      import reflect.runtime.universe._
      import reflect.ClassTag
      
      def classTag2[T: TypeTag]: ClassTag[T] = {
        ClassTag[T]( typeTag[T].mirror.runtimeClass( typeTag[T].tpe ) )
      }
      
      // example:
      
      def doSomething[T : TypeTag] = {
        val c = classTag2[T]
        c.runtimeClass.getName
      }
      
person gourlaysama    schedule 27.07.2013
comment
У меня другая проблема: как я могу переписать i.erasure, где i - неявный манифест [T] - person funnyF; 25.08.2013
comment
вам следует заменить это неявное Manifest[T] на неявное ClassTag[T] и вызвать i.runtimeClass. - person gourlaysama; 25.08.2013
comment
Спасибо, все исправлено, кроме одного: в одном методе у меня TypeTag T, а мне нужен кулон на manifest[T].erasure. Обычно я бы использовал classTag[T].runtimeClass, но на этот раз я не хочу переименовывать TypeTag в ClassTag (потому что метод является функцией применения, и многие методы вызывают эту функцию применения) - person funnyF; 28.08.2013
comment
@ user2625693, вы можете получить ClassTag от TypeTag, см. мою правку. - person gourlaysama; 30.08.2013
comment
@gourlaysama, если вы используете runtimeMirror(getClass.getClassLoader), вы подвержены ошибкам загрузки классов, потому что T (в конце концов, он указывается пользователем) может быть загружен любым загрузчиком классов, а не только тем, который загрузил класс с помощью метода classTag2. Лучше вместо этого использовать typeTag[T].mirror. - person Vladimir Matveev; 31.08.2013