Как полностью избежать отражения во время выполнения в 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так что должно быть очень быстро.

Другие вопросы по тегам