【问题标题】:Cannot get type of generic object in a list无法在列表中获取通用对象的类型
【发布时间】:2019-07-31 10:26:57
【问题描述】:

我有以下特点:

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) = {...}

如果不清楚,意思是MultiStorageC(或子类型)类型的元素存储在多个存储中,每个存储都包含单一类型的元素。

我正在使用泛型来实现 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 存储中拥有包含任意数量元素的多存储

【问题讨论】:

  • 假设我们有trait Configobject config1 extends Configobject config2 extends Configobject storage1 extends Storage[config1.type]object storage2 extends Storage[config2.type]val multiStorage = new MultiStorage[Config](List(storage1, storage2))那么multiStorage.register(config1)multiStorage.register(config2)是什么? s 都不是Storage[C] 类型,即Storage[Config]
  • 正如我所说,我可能滥用了泛型。在您提到的示例中,我希望multiStorage.register(config1) 内部调用storage1.register(config1)multiStorage.register(config2) 内部调用storage2.register(config2)

标签: scala generics types shapeless


【解决方案1】:

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

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

【讨论】:

  • 它看起来像我想要的,谢谢!但是,当我创建 FooStorage 时,它抱怨找不到 ctag 值(我在另一个文件中创建它)。我尝试遵循第二种方法,因为我的 trait 可以成为一个抽象类
【解决方案2】:

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 &lt;: Config。但是因为我们可以注册任何类型的配置 T &lt;: Config 它应该更像 Storage[T forAll { type T &lt;: Config }]Storage[[T &lt;: Config]T] 如果这样的语法存在于 Scala 但实际上 Scala 没有 rank-2 types

【讨论】:

    猜你喜欢
    • 2011-06-12
    • 1970-01-01
    • 1970-01-01
    • 2020-02-24
    • 1970-01-01
    • 2011-07-26
    • 1970-01-01
    • 2013-02-28
    • 2019-01-12
    相关资源
    最近更新 更多