Scala: Typecast без явно известного параметра типа
Рассмотрим следующий пример:
case class C[T](x:T) {
def f(t:T) = println(t)
type ValueType = T
}
val list = List(1 -> C(2), "hello" -> C("goodbye"))
for ((a,b) <- list) {
b.f(a)
}
В этом примере я знаю (гарантия времени выполнения), что типa
будет какой-тоT
, а такжеb
будет иметь типC[T]
с тем жеT
, Конечно, компилятор не может этого знать, поэтому мы получаем ошибку при набореb.f(a)
.
Чтобы сказать компилятору, что этот вызов в порядке, нам нужно выполнить typecast à lab.f(a.asInstanceOf[T])
, К несчастью,T
здесь не известно Итак, мой вопрос: как мне переписатьb.f(a)
чтобы этот код компилировался?
Я ищу решение, которое не включает в себя сложные конструкции (чтобы код был читабелен) и было бы «чистым» в том смысле, что мы не должны полагаться на стирание кода, чтобы оно работало (см. Первый подход ниже).
У меня есть несколько подходов к работе, но я нахожу их неудовлетворительными по разным причинам.
Подходы, которые я пробовал:b.asInstanceOf[C[Any]].f(a)
Это работает и достаточно читабельно, но основано на «лжи».b
не типC[Any]
и единственная причина, по которой мы не получаем ошибку времени выполнения, заключается в том, что мы полагаемся на ограничения JVM (стирание типов). Я думаю, что это хороший стиль только для использованияx.asInstanceOf[X]
когда мы знаем, чтоx
действительно типаX
.
b.f(a.asInstanceOf[b.ValueType])
Это должно работать в соответствии с моим пониманием системы типов. Я добавил участникаValueType
к классуC
для того, чтобы иметь возможность явно ссылаться на параметр типаT
, Однако при таком подходе мы получаем загадочное сообщение об ошибке:
Error:(9, 22) type mismatch;
found : b.ValueType
(which expands to) _1
required: _1
b.f(a.asInstanceOf[b.ValueType])
^
Зачем? Кажется, жаловаться, что мы ожидаем типа_1
но получил тип_1
! (Но даже если этот подход работает, он ограничен случаями, когда у нас есть возможность добавить членаValueType
вC
, ЕслиC
это некоторый существующий класс библиотеки, мы тоже не можем этого сделать.)
for ((a,b) <- list.asInstanceOf[List[(T,C[T]) forSome {type T}]]) {
b.f(a)
}
Это работает и семантически правильно (то есть, мы не "лжем" при вызовеasInstanceOf
). Ограничение в том, что это несколько нечитаемо. Кроме того, это несколько специфично для нынешней ситуации: еслиa,b
не приходят из того же итератора, то где мы можем применить этот тип приведения? (Этот код также имеет побочный эффект, который слишком сложен для Intelli / J IDEA 2016.2, что выделяет его как ошибку в редакторе.)
val (a2,b2) = (a,b).asInstanceOf[(T,C[T]) forSome {type T}]
b2.f(a2)
Я бы ожидал, что это сработает сa2,b2
теперь должны быть типыT
а такжеC[T]
для того же экзистенциальногоT
, Но мы получаем ошибку компиляции:
Error:(10, 9) type mismatch;
found : a2.type (with underlying type Any)
required: T
b2.f(a2)
^
Зачем? (Кроме того, у этого подхода есть недостаток в том, что он несет затраты времени выполнения (я думаю) из-за создания и уничтожения пары.)
b match {
case b : C[t] => b.f(a.asInstanceOf[t])
}
Это работает. Но заключение кода в соответствие делает код гораздо менее читабельным. (И это также слишком сложно для Intelli / J.)