Scala: Typecast sem parâmetro de tipo explicitamente conhecido
Considere o seguinte exemplo:
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)
}
Neste exemplo, eu sei (garantia de tempo de execução) que o tipo dea
será algumT
eb
terá tipoC[T]
com o mesmoT
. Obviamente, o compilador não pode saber disso, portanto, obtemos um erro de digitação emb.f(a)
.
Para dizer ao compilador que esta chamada está correta, precisamos fazer uma conversão de tipo àb.f(a.asInstanceOf[T])
. Infelizmente,T
não é conhecido aqui. Então, minha pergunta é: como reescreverb.f(a)
para fazer esse código compilar?
Estou procurando uma solução que não envolva construções complexas (para manter o código legível) e que seja "limpa" no sentido de que não devemos confiar na eliminação do código para fazê-lo funcionar (consulte a primeira abordagem abaixo).
Tenho algumas abordagens de trabalho, mas as acho insatisfatórias por várias razões.
Abordagens que tentei:b.asInstanceOf[C[Any]].f(a)
Isso funciona e é razoavelmente legível, mas é baseado em uma "mentira".b
não é do tipoC[Any]
, e a única razão pela qual não obtemos um erro de tempo de execução é porque dependemos das limitações da JVM (apagamento de tipo). Eu acho que é bom estilo só usarx.asInstanceOf[X]
quando sabemos quex
é realmente do tipoX
.
b.f(a.asInstanceOf[b.ValueType])
Isso deve funcionar de acordo com meu entendimento do sistema de tipos. Eu adicionei o membroValueType
para a aulaC
para poder se referir explicitamente ao parâmetro typeT
. No entanto, nessa abordagem, recebemos uma mensagem de erro misteriosa:
Error:(9, 22) type mismatch;
found : b.ValueType
(which expands to) _1
required: _1
b.f(a.asInstanceOf[b.ValueType])
^
Por quê? Parece reclamar que esperamos tipo_1
mas tem tipo_1
! (Mas, mesmo que essa abordagem funcione, ela se limita aos casos em que temos a possibilidade de adicionar um membroValueType
paraC
. E seC
é alguma classe de biblioteca existente, também não podemos fazer isso.)
for ((a,b) <- list.asInstanceOf[List[(T,C[T]) forSome {type T}]]) {
b.f(a)
}
Este funciona e é semanticamente correto (ou seja, não "mentimos" ao invocarasInstanceOf
) A limitação é que isso é um tanto ilegível. Além disso, é um pouco específico para a situação atual: sea,b
não vier do mesmo iterador, onde podemos aplicar esse tipo de conversão? (Esse código também tem o efeito colateral de ser muito complexo para o Intelli / J IDEA 2016.2, que o destaca como um erro no editor.)
val (a2,b2) = (a,b).asInstanceOf[(T,C[T]) forSome {type T}]
b2.f(a2)
Eu esperava que este funcionasse desdea2,b2
agora deve ter tiposT
eC[T]
para o mesmo existencialT
. Mas temos um erro de compilação:
Error:(10, 9) type mismatch;
found : a2.type (with underlying type Any)
required: T
b2.f(a2)
^
Por quê? (Além disso, a abordagem tem a desvantagem de incorrer em custos de tempo de execução (eu acho) devido à criação e destruição de um par.)
b match {
case b : C[t] => b.f(a.asInstanceOf[t])
}
Isso funciona. Mas encerrar o código com uma correspondência torna o código muito menos legível. (E também é muito complicado para Intelli / J.)