Почему мы должны явно указывать класс типов ClassTag
Теперь, когда scala перешел к исправлению стирания типа JVM с помощью ClassTag
класс типов, почему он включен, вместо того, чтобы компилятор всегда захватывал сигнатуру типа для проверки во время выполнения. Наличие неявного параметризованного ограничения типа позволило бы вызвать classTag[T]
независимо от объявления общего параметра.
РЕДАКТИРОВАТЬ: Я должен уточнить, что я не имею в виду, что Scala должен изменить подпись за кулисами, чтобы всегда включать ClassTag
, Скорее я имею в виду, что с ClassTag
показывает, что scala может собирать информацию о типе во время выполнения и, следовательно, избегать ограничений на стирание типов, почему этот захват не может быть неявным как часть компилятора, чтобы эта информация всегда была доступна в коде scala?
Я подозреваю, что это обратная совместимость, совместимость с Java-экосистемой, размер двоичного файла или накладные расходы времени выполнения, но это всего лишь предположение.
2 ответа
Обратная совместимость будет полностью разрушена, правда. Если у вас есть простой метод, такой как:
def foo[A](a: A)(implicit something: SomeType) = ???
Затем предположим, что в следующей версии Scala компилятор неожиданно добавил неявное ClassTag
с подписью всех методов с параметрами типа. Этот метод будет сломан. Везде это явно называлось как foo(a)(someTypeValue)
не будет работать больше Двоичная и исходная совместимость исчезла бы.
Совместимость Java была бы ужасной. Предположим, наш метод теперь выглядит так:
def foo[A : ClassTag](a: A) = ???
Так как ClassTag
s генерируются компилятором Scala, использование этого метода из Java оказалось бы более сложным. Вы должны создать ClassTag
сам.
ClassTag<MyClass> tag = scala.reflect.ClassTag$.MODULE$.apply(MyClass.class);
foo(a, tag);
Моя Java может быть не на 100% правильной, но вы поняли идею. Все параметризованное станет очень уродливым. Ну, это уже так, если это требует неявного ClassTag
, но класс методов, где это было бы необходимо, резко увеличился бы.
Более того, стирание типов не является большой проблемой в большинстве параметризованных методов, которые мы (по крайней мере, я) используем. Я думаю, что автоматически требует ClassTag
для каждого параметра типа было бы гораздо больше проблем, чем помогло бы, по вышеуказанным причинам.
Конечно, это добавит больше накладных расходов компилятора, так как потребуется генерировать больше ClassTag
чем обычно. Я не думаю, что это добавит намного больше времени выполнения, если только 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
на сайте вызова метода.
Да, вы бы оштрафовали любой универсальный метод или класс за случай использования, который встречается довольно редко (например, требуется построение массива или восстановление значений разнородных карт). С этой идеей "всегда тегов классов" вы также эффективно уничтожаете возможность вызова кода Scala из Java. В целом, просто не имеет смысла требовать, чтобы тег класса присутствовал всегда, ни с точки зрения совместимости, ни с точки зрения производительности, ни с точки зрения размера класса.