【问题标题】:Generic implementation should return subtype depending on subclass通用实现应根据子类返回子类型
【发布时间】:2015-12-09 17:40:16
【问题描述】:

在我的应用程序中有资源,其中许多以相同的方式加载,所以我想抽象方法。

我创建了一个名为Resources的特征

现在这个特征需要完成三个任务。

  1. 它为任意数量的Resources 实现了一个缓存。
  2. 它为所有可以加载该类型资源的加载器实现了一个映射。 (例如,对于可能包含“PNGLoader”、“JPGLoader”甚至“GIFLoader”的资源图像)
  3. 实现用于在给定路径检索资源的通用/默认方法

看起来像这样:

trait Resources
{
    private val loaders: mutable.HashMap[String, ResourceLoader] = new mutable.HashMap[String, ResourceLoader]
    private val cache: mutable.HashMap[String, Resource] = new mutable.HashMap[String, Resource]

    def get(path: String): Resource =
    {
        // Check cache... if fail, see if there is a loader registered
    }

    private def load(path: String): Resource =
    {
        // Check if there is a loader for the file, if so loader.load(path)
    }

    def registerLoader(...)
}

trait ResourceLoader
{
    def load(path: String): Resource
}

trait Resource

现在想象一下:

class Image extends Resource

object Images extends Resources

object PNGLoader extends ResourceLoader
{
    Textures.register("png", this)

    override def load(path: String): Image =
    {
        // Do some magic
    }
}

现在,有一个问题。这种方法可行(我已经尝试过),但问题是:

val img: Image = Images.get("images/test.png")

会导致类型不匹配,因为 Image != Resource。

所以我需要这样做

val img: Resource = Images.get("images/test.png")

或手动投射。两者都不是一个真正的选择。

现在,我尝试使用这样的东西:

trait Resources[ResourceType <: Resources[ResourceType]]
{
      private val cache: mutable.HashMap[String, ResourceType] = new mutable.HashMap[String, ResourceType]
   def get(path: String): ResourceType = {...}
}

trait ResourceLoader[ResourceType <: ResourceLoader[ResourceType]]
{
    def load(path: String): ResourceType
}

但这当然行不通,因为虽然类型命名完全一样,但没有共享信息,所以“ResourceType (of ResourceLoader) != ResourceType (of Resources)”所以loader.load(path) 不能工作。

总结:我想返回一个 ConcreteType(对于一个 ImageLoader 应该是一个 Image,对于一个 VideoLoader 这应该是一个 Video),但是有不同的类负责缓存,比如“图片”和“视频”,所以我可以这样做:

val video: Video = Videos.get(...)val image: Image = Images.get(...)

所以所有东西都应该有不同的缓存(这样我就可以刷新所有视频或所有图像等)

另外,我刚刚看到,我可能需要将 private 更改为 protected g

【问题讨论】:

    标签: scala generics inheritance polymorphism return-type


    【解决方案1】:

    您确实可以将ResourcesResourceLoader 设为通用,但上限可以更简单:A &lt;: Resource

    这样你可能会得到类似的结果:

    import scala.collection.mutable.HashMap
    
    trait Resource
    
    trait Resources[A <: Resource] {
      protected val cache = HashMap.empty[String, A] // protected for example below
      private val loaders = HashMap.empty[String, ResourceLoader[A]]
      def get(path: String) : A = cache.get(path).get // don't call .get on Option
      def registerLoader(loader: ResourceLoader[A]) = ???
    }
    
    trait ResourceLoader[A <: Resource] {
      def load(path: String): A
    }
    

    您可以将其与 Image 资源一起使用:

    class Image extends Resource
    
    object Images extends Resources[Image] {
      cache += "foo" -> new Image
    }
    
    Images.get("foo")
    // Image = Image@4ddf3cd9
    

    可以看到Images.get的结果类型是Image

    您只能注册ResourceLoader[A] 类型的加载器,因此您可以安全地调用loader.load(path),因为它还会返回A 类型的资源。

    【讨论】:

      猜你喜欢
      • 2014-12-19
      • 2013-10-17
      • 1970-01-01
      • 2015-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多