Не удается получить тип универсального объекта в списке - PullRequest
4 голосов
/ 09 марта 2019

У меня есть следующая черта:

trait Storage[C <: Config] {
  def get(name: String, version: Int): Option[C]
  def list: List[(String, String)]
  def register(config: C): Boolean
}

, и я хочу создать следующий класс:

class MultiStorage[C <: Config](storages: List[Storage[_ <: C]]) extends Storage[C] {
    def get(name: String, version: Int): Option[C] = {...}
    def list: List[(String, String)] = {...}    
    def register(config: C) = {...}

Если не ясно, это означает, что MultiStorage хранит элементытипа C (или подтипа) в нескольких хранилищах, каждое из которых содержит элементы одного типа.

Я борюсь с обобщениями для реализации метода регистра.Идея состоит в том, что в зависимости от типа объекта, который я хочу зарегистрировать, мне нужно выбрать правильное хранилище для регистрации, что-то вроде:

def register(config: C) = {
   storages.foreach(s => {
      if (typeOf(s) is Storage[C]) { // same type of config
           s.register(config)
           return
      }
   }) 
}

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

Одна из идей, которые я пробовал, имел метод в Storage, который возвращаеттипа:

protected def getType()(implicit tag: TypeTag[C]): universe.Type = typeOf[C]

но на стороне вызывающей стороны я смог получить в результате что-то вроде _$1, что я, честно говоря, не понимаю, что это значит.

Еще одна попытка былаиспользовать shapeless, но в этом случае я не уверен, возможно ли иметь мульти-хранилище, содержащее произвольное количество элементов в HList хранилище

Ответы [ 2 ]

2 голосов
/ 10 марта 2019

Необходимо обеспечить доступ к классу времени выполнения, например:

class Config
class FooConfig extends Config
class BarConfig extends Config

trait Storage[C <: Config] {
    val ctag: ClassTag[C]

    def get(name: String, version: Int): Option[C]
    def list: List[(String, String)]
    def register(config: C): Boolean
}

class FooStorage(implicit val ctag: ClassTag[FooConfig]) extends Storage[FooConfig] {
    override def get(name: String, version: Int): Option[FooConfig] = ???

    override def list: List[(String, String)] = ???

    override def register(config: FooConfig): Boolean = ???
}

class BarStorage(implicit val ctag: ClassTag[BarConfig]) extends Storage[BarConfig] {
    override def get(name: String, version: Int): Option[BarConfig] = ???

    override def list: List[(String, String)] = ???

    override def register(config: BarConfig): Boolean = ???
}


class MultiStorage[C <: Config](storages: List[Storage[_ <: C]])(implicit val ctag: ClassTag[C]) extends Storage[C] {
    def get(name: String, version: Int): Option[C] = ???

    def list: List[(String, String)] = ???

    def register(config: C): Boolean = {
        storages.foreach(storage => {
            if (storage.ctag.runtimeClass.isAssignableFrom(config.getClass)) {

            }
        })
        ???
    }
}

Поскольку у черт не может быть параметров конструктора, неявный тег класса необходимо повторять в каждом классе, реализующем черту.Если ваша классовая структура позволяет вместо Storage быть абстрактным классом, количество шаблонов может быть уменьшено:

abstract class Storage[C <: Config](implicit val ctag: ClassTag[C]) {

    def get(name: String, version: Int): Option[C]
    def list: List[(String, String)]
    def register(config: C): Boolean
}

class FooStorage extends Storage[FooConfig] {
    override def get(name: String, version: Int): Option[FooConfig] = ???

    override def list: List[(String, String)] = ???

    override def register(config: FooConfig): Boolean = ???
}
0 голосов
/ 10 марта 2019

Возможный подход с Shapeless

import shapeless.{::, HList, HNil}

object App {
  trait Config
  object config1 extends Config
  object config2 extends Config

  trait Storage[C <: Config] {
    def get(name: String, version: Int): Option[C]
    def list: List[(String, String)]
    def register(config: C): Boolean
  }

  object storage1 extends Storage[config1.type] {
    override def get(name: String, version: Int): Option[config1.type] = ???
    override def list: List[(String, String)] = ???
    override def register(config: config1.type): Boolean = {
      println("storage1#register")
      true
    }
  }
  object storage2 extends Storage[config2.type] {
    override def get(name: String, version: Int): Option[config2.type] = ???
    override def list: List[(String, String)] = ???
    override def register(config: config2.type): Boolean = {
      println("storage2#register")
      true
    }
  }

  class MultiStorage[L <: HList](storages: L) /*extends Storage[C]*/ {
//    def get(name: String, version: Int): Option[C] = ???
    def list: List[(String, String)] = ???
    def register[C <: Config](config: C)(implicit find: Find[C, L]): Boolean = find(config, storages).register(config)
  }

  trait Find[C <: Config, L <: HList] {
    def apply(config: C, l: L): Storage[C]
  }

  trait LowPriorityFind {
    implicit def tail[C <: Config, L <: HList, C1 <: Config, T <: HList](implicit
      ev: L <:< (Storage[C1] :: T),
      find: Find[C, T]): Find[C, L] = (config, l) => find(config, l.tail)
  }

  object Find extends LowPriorityFind {
    implicit def head[C <: Config, L <: HList, T <: HList](implicit 
      ev: L <:< (Storage[C] :: T)): Find[C, L] = (_, l) => l.head
  }

  val multiStorage = new MultiStorage(storage1 :: storage2 :: HNil)

  def main(args: Array[String]): Unit = {
    multiStorage.register(config1) // storage1#register
    multiStorage.register(config2) // storage2#register
  }
}

Создание MultiStorage расширенного Storage может быть слишком ограничительным. Мы могли бы написать

class MultiStorage(storages: List[Storage[_ <: Config]] /*i.e. List[Storage[T] forSome { type T <: Config}]*/) 
  extends Storage[T forSome { type T <: Config }] /*i.e. just Storage[Config]*/

т.е. Storage неизвестного типа T <: Config. Но так как мы можем зарегистрировать конфиги любого типа T <: Config, это будет больше похоже на Storage[T forAll { type T <: Config }] или Storage[[T <: Config]T], если такой синтаксис существует в Scala, но на самом деле Scala не имеет типов ранга 2 .

...