Я полагаю, что вы столкнулись с двумя различными ошибками в Scala-Guice, одна из которых еще не исправлена (и, возможно, даже еще не отправлена).
Чтобы описать эти проблемы, мне нужно кратко представить, как Guice иСкала-Guice работа.По сути, то, что делает Guice, - это отображение типа в метод фабрики для объекта этого типа.Для поддержки некоторых расширенных типов функций они сопоставляются с некоторым внутренним представлением «ключей», а затем для каждого «ключа» Гис создает способ создания соответствующего объекта.Также важно, чтобы дженерики в Java были реализованы с использованием стирания типов.Вот почему, когда вы пишете что-то вроде:
bind(classOf[SttpBackend[Try, Nothing]]).toProvider(classOf[SttpBackendProvider])
в raw-Guice, «ключ» фактически становится чем-то вроде «com.softwaremill.sttp.SttpBackend».К счастью, разработчики Guice подумали об этой проблеме с помощью дженериков и представили TypeLiteral[T]
, чтобы вы могли передавать информацию о дженериках.
Система типов Scala более обширна, чем в Java, и имеет некоторые лучшиеподдержка отражения от компилятора.Scala-Guice использует его для автоматического сопоставления типов Scala с этими более подробными ключами.К сожалению, он не всегда работает идеально.
Первая проблема - результат того факта, что тип SttpBackend
определен как
trait SttpBackend[R[_], -S]
, поэтому он использует ожидаемый первый параметрбыть конструктором типов;и что первоначально Scala-Guice использовал инфраструктуру scala.reflect.Manifest
.AFAIU, такие типы более высокого типа не могут быть представлены как Manifest
, и именно об этом действительно говорит ошибка в вашем вопросе.
К счастью, Scala добавила новую инфраструктуру scala.reflect.runtime.universe.TypeTag
для решения этой проблемы.проблема в лучшем и более последовательном виде, и Scala-Guice перешел на его использование.Вот почему в более новой версии Scala-Guice ошибка компилятора исчезает.К сожалению, в Scala-Guice есть еще одна ошибка, которая приводит к сбою кода во время выполнения, и это отсутствие обработки типа Nothing
Scala.Видите ли, тип Nothing
является своего рода подделкой в JVM.Это одна из вещей, где система типов Scala более обширна, чем система Java.В мире JVM нет прямого сопоставления для Nothing
.К счастью, нет способа создать какое-либо значение типа Nothing
.К сожалению, вы все еще можете создать classOf[Nothing]
.Компилятор Scala-to-JVM обрабатывает его с помощью искусственного scala.runtime.Nothing$
.Он не является частью общедоступного API, это детали реализации, в частности, Scala над JVM.В любом случае это означает, что тип Nothing
нуждается в дополнительной обработке при преобразовании в Guice TypeLiteral
, и его нет.Существует для Any
двоюродного брата Nothing
, но не для Nothing
(см. Использование anyType
в TypeConversions.scala ).
Так что на самом деле дваОбходные пути:
- Используйте сырой синтаксис на основе Java для Guice вместо красивого Scala-Guice:
bind(new TypeLiteral[SttpBackend[Try, Nothing]]() {})
.toInstance(sttpBackend) // or to whatever
См. онлайн-демонстрация * на основе 1050 *на вашем примере.
Исправьте TypeConversions.scala в Scala-Guice, как в:
private[scalaguice] object TypeConversions {
private val mirror = runtimeMirror(getClass.getClassLoader)
private val anyType = typeOf[Any]
private val nothingType = typeOf[Nothing] // added
...
def scalaTypeToJavaType(scalaType: ScalaType): JavaType = {
scalaType.dealias match {
case `anyType` => classOf[java.lang.Object]
case `nothingType` => classOf[scala.runtime.Nothing$] //added
...
Я попробовал это локально, и, похоже, это исправляет ваш пример.Я не проводил обширных тестов, так что это могло сломать что-то еще.