Предположим, у нас есть универсальный тип (например, Seq[E]
) и конкретный подтип (например, Seq[Int]
). Как мы можем извлечь конкретный тип, который соответствует параметрам типа абстракции. Другими словами, откуда мы можем знать E -> Int
.
Ниже приведен минимальный пример кода, который проверяет желаемое поведение. Функция extractTypeBinding
выполнит рассматриваемое преобразование.
import scala.reflect.runtime.{universe => ru}
class MyFuncs
object MyFuncs {
def fn1[E](s: Seq[E]): E = ???
def fn2[K, V](m: Map[K, V]): Int = ???
}
object Scratch {
def extractTypeBinding(genType: ru.Type, typeParam: ru.Type)(concreteType: ru.Type): ru.Type = ???
def getArgTypes(methodSymbol: ru.MethodSymbol): Seq[ru.Type] =
methodSymbol.paramLists.headOption.getOrElse(Nil).map(_.typeSignature)
def main(a: Array[String]): Unit = {
// Grab the argument types of our methods.
val funcsType = ru.typeOf[MyFuncs].companion
val fn1ArgTypes = getArgTypes(funcsType.member(ru.TermName("fn1")).asMethod)
val fn2ArgTypes = getArgTypes(funcsType.member(ru.TermName("fn2")).asMethod)
val genericSeq = fn1ArgTypes.head // Seq[E]
val genericMap = fn2ArgTypes.head // Map[K, V]
// Create an extractor for the `E` in `Seq[E]`.
val seqElExtractor = extractTypeBinding(genericSeq, genericSeq.typeArgs.head) _
// Extractor for the `K` in `Map[K,V]`
val mapKeyExtractor = extractTypeBinding(genericMap, genericMap.typeArgs.head) _
// Extractor for the `V` in `Map[K,V]`
val mapValueExtractor = extractTypeBinding(genericMap, genericMap.typeArgs(1)) _
println(seqElExtractor(ru.typeOf[Seq[Int]])) // should be Int
println(seqElExtractor(ru.typeOf[Seq[Map[String, Double]]])) // should be Map[String, Double]
println(mapKeyExtractor(ru.typeOf[Map[String, Double]])) // should be String
println(mapKeyExtractor(ru.typeOf[Map[Int, Boolean]])) // should be Int
println(mapValueExtractor(ru.typeOf[Map[String, Double]])) // should be Double
println(mapValueExtractor(ru.typeOf[Map[Int, Boolean]])) // should be Boolean
}
}
Судя по строкам документации, кажется, что asSeenFrom
должен быть ключом к реализации extractTypeBinding
. Я попробовал приведенную ниже реализацию, но она вернула параметр типа без изменений.
def extractTypeBinding(genType: ru.Type, typeParam: ru.Type)(concreteType: ru.Type): ru.Type =
typeParam.asSeenFrom(concreteType, genType.typeSymbol.asClass)
...
println(seqElExtractor(ru.typeOf[Seq[Int]])) // E
println(seqElExtractor(ru.typeOf[Seq[Map[String, Double]]])) // E
Если asSeenFrom
правильный подход, то каким будет правильное заклинание? Если нет, то как это нужно сделать?
extractTypeBinding
? Что не так сconcreteType.typeArgs.head
? - person Dmytro Mitin   schedule 07.12.2020concreteType.typeArgs.tail.head
для параметра 2-го типаMap
. - person Dmytro Mitin   schedule 07.12.2020