Извлечение типа в Scala

Я новичок в Scala и продвинутых языках программирования. Пытаюсь решить следующую задачу.

У меня есть:

val s: Seq[SomeMutableType[_]]

Я предполагаю, что все элементы в последовательности имеют один и тот же тип (но пока не знаю, какой именно).

Как я могу позвонить:

def proc[T](v0: SomeMutableType[T], v1: SomeMutableType[T]) { /* ... */ }

с чем-то вроде

proc(s(0), s(1))

Компилятор жалуется:

  • несоответствие типов; найдено : SomeMutableType[_$351], где требуется тип _$351: SomeMutableType[Any] Примечание: _$351 ‹: Любой, но класс SomeMutableType является инвариантным в типе T. Вместо этого вы можете определить T как +T. (SLS 4.5)

Я думал об этой ковариантной штуке, но не думаю, что в моем случае это имеет смысл. Я просто хочу, чтобы компилятор поверил мне, когда я говорю, что s(0) и s(1) одного типа! Обычно я делаю это с помощью некоторого приведения, но здесь я не могу привести к SomeMutableType[T], так как T неизвестен из-за стирания. Конечно, я не могу изменить определение proc.


person scand1sk    schedule 09.05.2011    source источник
comment
Как получить s? В идеале вы должны привязать свой _ к некоторому параметру типа, объявленному в методе или классе…   -  person Jean-Philippe Pellet    schedule 09.05.2011
comment
На самом деле, это довольно сложно. SomeMutableType параметризуется, потому что это имеет смысл параметризовать. Однако s — это атрибут класса C, который обычно не заботится о параметре, за исключением моего конкретного случая использования (где все элементы должны быть одного типа). Я могу предположить, что в моем экземпляре C все элементы s одного типа, но в общем случае это неверно. Возможно, на этом этапе мне следует использовать какой-нибудь подкласс C, но это сделало бы какой-то другой код довольно сложным (ввести фабричные методы и т. д.), поскольку классы генерируются синтаксическим анализатором.   -  person scand1sk    schedule 09.05.2011
comment
Я не следил за типом, но я знаю, что это X. Затем приведите его к X и проявите свое предположение в коде. Если вы не хотите бросать, вы ДОЛЖНЫ следить за типом. Это так просто!   -  person ziggystar    schedule 22.09.2011


Ответы (2)


Проблема в том, что вы действительно не можете дать такую ​​гарантию. Например:

scala> import scala.collection.mutable.Buffer
import scala.collection.mutable.Buffer

scala> val s: Seq[Buffer[_]] = Seq(Buffer(1), Buffer("a"))
s: Seq[scala.collection.mutable.Buffer[_]] = List(ArrayBuffer(1), ArrayBuffer(a))

Видеть? Вы не знаете, что s(0) и s(1) относятся к одному и тому же типу, потому что они могут быть разных типов.

На этом этапе вы должны задать вопрос о том, чего вы хотите достичь, вместо того, чтобы спрашивать, как решить проблему, как вы хотите ее выполнить. То, что вы выбрали, не сработает. Сделайте шаг назад, подумайте, какую проблему вы пытались решить с помощью этого подхода, и спросите, как решить эту проблему.

Например, вы говорите:

Я предполагаю, что все элементы в последовательности имеют один и тот же тип (но пока не знаю, какой именно).

Возможно, вы хотите параметризовать класс или метод и использовать его параметр типа при объявлении s. Или, может быть, вообще не иметь s.

person Daniel C. Sobral    schedule 09.05.2011
comment
Я знаю, что я не знаю, что s(0) и s(1) одного и того же типа, но я был бы счастлив иметь ClassCastException или что-то подобное, если бы это не было так, когда я вызываю proc. Параметр типа моего SomeMutableType полезен в некоторых частях моего приложения, но здесь меня это не особо волнует. - person scand1sk; 10.05.2011
comment
На самом деле я пытаюсь здесь смоделировать гиперграф: SomeMutableType — это узлы, которые инкапсулируют набор типизированных значений. s — это последовательность узлов, связанных гиперребром. В соответствии с некоторыми свойствами (которые вряд ли можно вычислить при создании гиперребра) я могу объединить все узлы одного заданного гиперребра в один выбранный узел и отбросить гиперребро. Значения этого единственного узла сводятся к пересечению наборов, инкапсулированных всеми узлами. Метод, который вычисляет пересечение, предполагает, что все значения имеют один и тот же тип, отсюда и моя проблема… - person scand1sk; 10.05.2011
comment
@scand1sk, почему бы тебе тогда не перевести его на Seq[SomeMutableType[AnyRef]]? Или, если вы знаете тип во время выполнения этого вызова, используйте collect для создания коллекции известного типа? - person Daniel C. Sobral; 10.05.2011
comment
Хорошо, вот тут-то и сказывается мое плохое знание Scala. Я действительно не знаю реальной разницы между [_], [Any] и [AnyRef]. У вас есть информация об этом, пожалуйста? - person scand1sk; 10.05.2011
comment
@scand1sk Ну, [_] будет иногда означать [Any], но вам действительно никогда не следует использовать его в этом значении. Вместо этого оставайтесь в безопасности и всегда говорите [Any], если это то, что вы имеете в виду. Другое значение [_], как здесь, — это экзистенциальный тип. Seq[Buffer[_]] — это сокращение от Seq[Buffer[t]] forSome { type t }. Это означает, что параметр типа Buffer является неопределенным типом t. Any является супертипом всех типов, включая примитивы. AnyRef похож на Object в Java. - person Daniel C. Sobral; 10.05.2011
comment
@scand1sk Здесь я использовал AnyRef, потому что я знаю, что стирание типа превращает все в AnyRef во время выполнения - и даже примитивы будут упакованы. Итак, как бы то ни было, Buffer будет содержать AnyRef. - person Daniel C. Sobral; 10.05.2011
comment
@scand1sk Обратите внимание, что любое использование asInstanceOf является изменением для внесения ошибок. Например, вызываемая функция может хранить объект другого типа, что может вызвать исключение в другом месте. - person Daniel C. Sobral; 10.05.2011
comment
@scand1sk Для получения дополнительной информации о типах см. экзистенциальные типы, а также Any, AnyRef, Null и Nothing или иерархию типов Scala. - person Daniel C. Sobral; 10.05.2011
comment
Для будущих читателей Даниил сделал опечатку. A[B[_]] на самом деле является сокращением от A[B[C] forSome { type C; }], а не A[B[C]] forSome { type C; }. - person HTNW; 26.12.2016

Я новичок в Scala, но, насколько я понимаю, ваша проблема заключается в использовании параметра типа подстановочного знака при объявлении s:

val s: Seq[SomeMutableType[_]]

Насколько я понимаю, стирание типов будет происходить всегда, и здесь вам действительно нужен параметризованный тип, привязанный к тому месту, где инициализируется s.

Например:

scala> class Erased(val s: List[_])
defined class Erased

scala> new Erased(List(1,2,3)).s.head
res21: Any = 1

Если вместо этого вы используете

scala> class Kept[T](val s: List[T])
defined class Kept

scala> new Kept(List(1,2,3)).s.head
res22: Int = 1

Затем содержимое s сохраняет информацию о своем типе, поскольку оно привязано к T. Т.е. именно так вы сообщаете компилятору, что «s(0) и s(1) одного типа».

person David McLaughlin    schedule 09.05.2011