Как получить класс [_ <: A]] с нижней границей - PullRequest
1 голос
/ 03 марта 2020

Здравствуйте) У меня есть один абстрактный класс и несколько конкретных классов в Java

abstract class SpecificRecordBase {}
class A extends SpecificRecordBase 
class B extends SpecificRecordBase
....

Также у меня есть карта в Scala. Я хочу поставить ключ String и значение в качестве класса, чтобы использовать его в методе:

def test(m: Map[String, Class[_ <: SpecificRecordBase]]):Unit = ???

Я пытался ClassTag (ClassLoader.getSystemClassLoader.loadClass (stringClassName)). RuntimeClass, но получил ошибку. Также я пытался создать метод и использовать его

  def getClass[T <: SpecificRecordBase](ct: ClassTag[T]): Class[T] = ct.runtimeClass

And also have an error
 Error:(10, 73) type mismatch;
 found   : Class[_$1] where type _$1
 required: Class[T]
  def getClass[T <: SpecificRecordBase](ct: ClassTag[T]): Class[T] = ct.runtimeClass

Не могли бы вы помочь мне, пожалуйста?

Ответы [ 2 ]

2 голосов
/ 08 марта 2020

Попробуйте asSubclass вот так

def getSubclass[T <: SpecificRecordBase](implicit ct: ClassTag[T]): Class[_ <: SpecificRecordBase] =
  ct.runtimeClass.asSubclass(classOf[SpecificRecordBase])

getSubclass[A]
getSubclass[B]
// res0: Class[_ <: SpecificRecordBase] = class A
// res1: Class[_ <: SpecificRecordBase] = class B

Готово asSubclass Java Реализация аналогична Алексею Scala один

public <U> Class<? extends U> asSubclass(Class<U> clazz) {
    if (clazz.isAssignableFrom(this))
        return (Class<? extends U>) this;
    else
        throw new ClassCastException(this.toString());
}
2 голосов
/ 03 марта 2020

Проблема заключается в подписи runtimeClass:

def runtimeClass: Class[_]

Класса, представляющего тип U, для которого будет удален T. Обратите внимание, что между T и U нет отношения подтипов.

Причина в таких типах, как Any, Nothing, et c. Но на самом деле ClassTag здесь бесполезен, потому что вы делаете ClassTag(<something>).runtimeClass, который можно упростить до <something>.

Так что просто разыграйте его:

ClassLoader.getSystemClassLoader.loadClass(stringClassName).asInstanceOf[Class[_ <: SpecificRecordBase]]

(РЕДАКТИРОВАТЬ: как указано Марио Гали c, это должно быть

ClassLoader.getSystemClassLoader.loadClass(stringClassName).asSubclass(classOf[SpecificRecordBase])

Аналогичные изменения применяются ниже.)

или даже проще

Class.forName(stringClassName).asSubclass(classOf[SpecificRecordBase])

Если вы не уверены, получите ли вы подтип SpecificRecordBase, вы можете проверить его:

val cls = Class.forName(stringClassName)
Try { cls.asSubclass(classOf[SpecificRecordBase]) } match {
  case Success(cls1) =>
    // handle a subclass
  case Failure(_) =>
    // handle a non-subclass
}

или без обработки исключений

val cls = Class.forName(stringClassName)
if (classOf[SpecificRecordBase].isAssignableFrom(cls)) {
  val cls1 = cls.asInstanceOf[Class[_ <: SpecificRecordBase]]
  // handle a subclass
} else {
  // handle a non-subclass
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...