无法获取列表中通用对象的类型

时间:2019-03-09 16:12:31

标签: scala generics types shapeless

我具有以下特征:

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(或子类型)的元素,每个存储中都包含一种类型的元素。

我正在与泛型作斗争以实现register方法。这个想法是,根据我要注册的对象的类型,我需要选择合适的存储空间进行注册,例如:

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 个答案:

答案 0 :(得分:2)

需要使运行时类可用,例如:

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 = ???
}

答案 1 :(得分:0)

使用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配置,因此如果Scala中存在这样的语法,但实际上Scala没有rank-2 types,则应该更像Storage[T forAll { type T <: Config }]Storage[[T <: Config]T]。 / p>