Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Почему мы должны явно указывать класс типов ClassTag

Автор вопроса: Arne ClaassenОтветов: 2

Теперь, когда scala перешла к исправлению стирания типа JVM с классом типов ClassTag, почему это необязательно, а не то, что компилятор всегда собирает сигнатуру типа для проверки во время выполнения. Наличие неявного параметризованного ограничения типа позволило бы вызывать classTag[T] независимо от объявления универсального параметра.

EDIT: я должен уточнить, что я не имею в виду, что scala должна изменять подпись за кулисами, чтобы всегда включать ClassTag. Скорее я имею в виду, что, поскольку ClassTag показывает, что scala может собирать информацию о типах во время выполнения и, следовательно, избегать ограничений на стирание типов, почему этот захват не может быть неявным как часть компилятора, чтобы эта информация всегда была доступна в коде scala?

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

scalascala-reflecttypeclass

Ответы

Ответ 1 · рейтинг 3 · Michael Zajac

На самом деле обратная совместимость будет полностью уничтожена. Если у вас есть простой метод, например:

def foo[A](a: A)(implicit something: SomeType) = ???

Затем предположим, что в следующей версии Scala компилятор вдруг добавил неявные ClassTag в сигнатуру всех методов с параметрами типа. Этот метод будет нарушен. Везде, где он явно вызывался, как foo(a)(someTypeValue), больше не будет работать. Двоичная и исходная совместимость исчезнет.

Совместимость с Java была бы уродливой. Предположим, что наш метод теперь выглядит так:

def foo[A : ClassTag](a: A) = ???

Поскольку ClassTag создаются компилятором Scala, использовать этот метод из Java будет сложнее. Вам придется создать ClassTag самостоятельно.

ClassTag<MyClass> tag = scala.reflect.ClassTag$.MODULE$.apply(MyClass.class);
foo(a, tag);

Моя Java может быть не на 100% правильной, но вы поняли. Все, что параметризовано, станет очень уродливым. Ну, это уже так, если для этого требуется неявный ClassTag, но класс методов, где это было бы необходимо, резко увеличился бы.

Кроме того, стирание типов не является большой проблемой в большинстве параметризованных методов, которые мы (по крайней мере, я) используем. Я думаю, что автоматическое требование ClassTag для каждого параметра типа было бы гораздо более проблематичным, чем помогло бы, по вышеуказанным причинам.

Конечно, это добавит дополнительных накладных расходов компилятору, так как потребуется генерировать больше ClassTags, чем обычно. Я не думаю, что это добавит гораздо больше накладных расходов во время выполнения, если только ClassTag не изменит ситуацию. Например, в простом методе, как показано ниже, ClassTag ничего не делает:

def foo[A : ClassTag](a: A): A = a

Мы также должны отметить, что они тоже не идеальны. Таким образом, их добавление не является окончательным решением проблем стирания.

val list = List(1, "abc", List(1, 2, 3), List("a", "b"))
def find[A: ClassTag](l: List[Any]): Option[A] =
    l collectFirst { case a: A => a }

scala> find[List[String]]
res2: Option[List[String]] = Some(List(1, 2, 3)) // Not quite! And no warnings, either.

Добавление ClassTag к каждому отдельному экземпляру класса добавит накладные расходы и, конечно же, нарушит совместимость. Это также во многих местах невозможно. Мы не можем просто смешать java.lang.String с ClassTag. Кроме того, мы по-прежнему были бы так же восприимчивы к стиранию. Наличие поля ClassTag в каждом классе на самом деле не лучше, чем использование getClass. Мы могли проводить сравнения, например

case a if(a.getClass == classOf[String]) => a.asInstanceOf[String]

Но это ужасно уродливо, требует гипса и не обязательно предназначено для исправления ClassTag. Если бы я попробовал что-то подобное с моим методом find, это бы не сработало - вообще.

// Can't compile
def find[A](l: List[Any]): Option[A] =
    l collectFirst { case a if(a.getClass == classOf[A]) => a.asInstanceOf[A] }

Даже если бы я приспособил это для работы с ClassTag, откуда бы это взялось? Я не мог сказать a.classTag == classTag[A], потому что A уже стерто. Мне нужно ClassTag на сайте вызова метода.

Ответ 2 · рейтинг 2 · 0__

Да, вы бы оштрафовали любой общий метод или класс за случай использования, который встречается довольно редко (например, требующий построения массива или восстановления разнородного значения карты). С этой идеей "всегда использовать теги класса" вы также эффективно уничтожите возможность вызова кода Scala из Java. Летом просто не имеет смысла требовать, чтобы тег класса всегда присутствовал, ни с точки зрения совместимости, ни с точки зрения производительности, ни с точки зрения размера класса.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы