Как полностью избежать отражения во время выполнения в Scala?
Обратите внимание, что это должен быть пост сообщества, и при необходимости следует добавлять примеры. Если вы не можете напрямую отредактировать ответ, чтобы добавить примеры (примеры проблем или решения), оставьте комментарий в виде ссылки на суть (или аналогичный) или добавьте отдельный ответ, который будет интегрирован позже.
Существует вероятность того, что Scala 3 может не включать scala.reflect.runtime
вообще (Дотти в настоящее время нет, и планы сделать это не уверены). Хотя ответы, применимые как к Scala 2, так и к Dotty, могут быть предпочтительнее для целей перехода и для немедленного улучшения производительности, также приветствуются решения для Dotty.
Рекомендации
https://www.cakesolutions.net/teamblogs/ways-to-pattern-match-generic-types-in-scala
1 ответ
Общие рекомендации
TypeTag
Они генерируются во время компиляции (которые могут иметь значительные накладные расходы во время компиляции из-за расширения макроса на каждом сайте использования) и используются во время выполнения, также генерируя некоторые потенциальные издержки времени выполнения, в зависимости от точного характера их использования. Таким образом, даже в Scala 2 их, вероятно, следует использовать только в крайнем случае (и мы надеемся решить все такие проблемы здесь, чтобы в последнем случае не было необходимости). Для сравнения, вещи, которые используют instanceOf
прямо или косвенно очень быстро.
Использовать отражение Java
instanceOf
это супер быстроclassOf
(т.е. Java getClass
) почти так же быстро.
Используйте ClassTag
Ссылочное равенство на ClassTag
Также должно быть очень быстро.
Используйте обернутый тип в качестве экземпляра класса типа
Когда это возможно, вы можете рассмотреть возможность помещения вашего типа в класс, чтобы придать ему конкретный тип "Java". Хотя часто возникают накладные расходы, вы можете использовать классы значений.
Классы типов в обернутом классе часто являются хорошим способом раскрытия функциональности. Кроме того, как указал @Blaisorblade, "теги типа просто класс беззаконного типа (с методами typeName: String
а также tpe: Type
) + материализация экземпляра ". Что подводит нас к следующему варианту:
При необходимости использует макросы
(в настоящее время не поддерживается в Dotty, но планируется)
Хотя, возможно, немного сложнее привыкнуть, конечный результат должен быть более чистым синтаксисом в коде, использующем макрос, чем если бы вы использовали TypeTag
, Кроме того, макросы используются далеко за пределами TypeTag
,
Избранные примеры
Соответствует параметру типа коллекции
пример
Типичный вариант использования для TypeTag
взято из популярного поста Scala: Что такое TypeTag и как его использовать? должен выполнить специальный полиморфизм для типа коллекции:
import scala.reflect.runtime.universe._
def meth[A : TypeTag](xs: List[A]) = typeOf[A] match {
case t if t =:= typeOf[String] => "list of strings"
case t if t <:< typeOf[Foo] => "list of foos"
}
scala> meth(List("string"))
res67: String = list of strings
scala> meth(List(new Bar))
res68: String = list of foos
В отличие от ClassTag
, TypeTag
является отражением во время выполнения. Может быть, я использую это неправильно здесь, хотя поведение довольно удивительно. По крайней мере, в REPL я не получаю никаких предупреждений со следующим:
def meth[A : ClassTag](xs: List[A]) = xs match {
case xs: List[String] => "list of strings"
case xs: List[Foo] => "list of foos"
}
meth(List(new Bar))
res10: String = "list of strings"
Решение
Это из @smarter на gitter (предполагается, что нам не нужно обрабатывать пустые списки разных типов отдельно):
def meth[A](xs: List[A]) = xs match {
case Nil => "nil"
case (_: String) :: _ => "list of strings"
case (_: Foo) :: _ => 'list of foos"
}
Это использует instanceOf
так что должно быть очень быстро.