首页 文章

Scala中的泛型集合有问题

提问于
浏览
3

我很困惑如何在Scala中解决这个问题 . 在Java中,我放弃了泛型或使用强制转换,但Scala比这更严格 . 这就是我所拥有的 .

  • 一个抽象基类 Factory[+F <: Factory[F]] 和一些与此无关的具体实现 . 这是协变的 . 这个类有一个创建产品的方法 - 见下一点 .

  • 表示工厂产品的抽象基类 Product[+F <: Factory[F]] . 这些也是协变和只读的 . 任何特定工厂只 生产环境 一种产品(即没有多种不同的子类型--FooFactory 生产环境 FooProduct,BarFactory 生产环境 BarProduct等)

  • 我必须有一个包含具体实例的工厂集合(Capt . 明显在这里说) . 目前这是 Iterable[Factory[_]] . 这是麻烦的第一部分 . Scala似乎将 _ 理解为 Any ,忽略了类型本身的约束 F <: Factory[F] .

  • 我有一个聚合工厂方法,要求每个工厂在给定相同参数的情况下 生产环境 其产品 . 结果是工厂到他们的产品的 Map ,目前编码为 Map[Factory[_],Product[_]] ,并尝试了 Map[Factory[_],Product[Factory[_]]] . 这是我遇到麻烦的另一部分 . 第一个 _ 与第二个 _ 取消链接,两者似乎都隐含为 Any .

我不能做的是创建该 Map . 它可能或不重要,但我通过使用另一种看起来像语法结构的方法来做(并且必须这样做) . 它来自一个特点:

  • 调用获取工厂集合的声明方法(在此称为 gather[B](fun: A => B): Map[A,B] ,未在特征本身中实现并通常输入) . 实际的工厂类型是通用的和未知的 .

  • 迭代该泛型集合并执行传递给它的函数 . 这个函数实际上是调用工厂方法的 .

  • 返回F到P的不可变 Map

编译器麻烦不在'gather'方法中,而是在调用它的代码中 . 我无法将其结果转换为 Map[Factory[_],Product[_]] ,因为_在任何一个地方都不符合 F <: Factory[F] ...我非常乐意使用 _ 的基本类型,但那是 Factory[Factory[Factory[Factory......(infinitely many times)]]]]]].... 我不知道是什么去做 .

请帮忙!

学习者

4 回答

  • 1

    大多数情况下,在类型中使用 _ 是不对的,除非你知道你在做什么 . 如果你不知道自己在做什么 . 并不是说Scala认为 _Any ,它知道 _ 可以代表任何东西 - 无论是 AnyNothing 还是介于两者之间的任何东西 .

    总结一下, Iterable[Factory[_]] 表示 Iterable[Factory[T]] forSome { type T } . 您可以将其显式写为 Iterable[Factory[T]] forSome { type T <: Factory[T] } 以获得所需的约束,就像您可以将其写为 Map[Factory[T], Product[T]] forSome { type T <: Factory[T] } 一样 .

    不过,我不确定这是否真的是你需要的 . 您的问题主要集中在如何解决问题,而不是专注于如何解决问题 . 例如,可能更好的是使用以下代替泛型:

    abstract class Factory {
        type T <: Factory
    }
    
  • 5

    如果我的Scala有点生疏,请原谅我 . 我写的是一般的OOP和泛型意义 .

    目前尚不清楚为什么 FactoryProduct 需要完全是泛型,很多协方差 . 无论是否实际需要,有一个简单的非泛型类 FactoryBase 是必要和充分的,其中所有 Factory 类最终都继承 . 你坚持到 Iterable[FactoryBase] .

    还不清楚为什么 Product 类型包含有关 Factory 的任何信息 . 如果确实需要这些信息,那么相反的意义就更有意义了 . Factory 产生 Product ,因此,知道它,所以这种知识可能会反映在类型中 . 然后,它可能不会 .

    如果您需要通用性,协方差和完整类型信息,您可以使用这样的层次结构:

    FactoryBase
       Factory[+F <: Factory[F,P], +P <: Product[F,P]] <: FactoryBase
          FooFactory <: Factory[FooFactory, FooProduct]
    ProductBase
       Product[+F <: Factory[F,P], +P <: Product[F,P]] <: ProductBase
          FooProduct <: Product[FooFactory, FooProduct]
    

    您可以随意省略任何不需要的类型参数 .

    还不清楚为什么需要将工厂映射到产品的 Map . Map 通常将名称之类的内容映射到实体 . 也就是说,给定名称,找到具有该名称的现有实体 . 工厂凭空创造新的 . 无论如何,如果你确实需要这样的 Map ,你可以使用 Map[FactoryBase, ProductBase] . 这不提供静态保证 FooFactory 映射到 FooProduct 而不是 BarProduct ,但 Map 不能提供 . 然后,你可以在其各自的 Factory 中粘贴一个 Product . 你已经知道编译时的映射,没有必要运行时 Map . 在 n ,产品 生产环境 后不需要与任何工厂相关联 .

    也许我完全误解了你,你需要详细说明你的设计 . 因此,它的目的和架构根本不明确 .

  • 2

    我不太确定我理解你的问题,但尝试使用类似的东西:

    gather[A : Factory, B : Factory](fun: A => B): Map[A,B]
    
  • 1

    添加另一个答案而不是编辑旧答案 .

    我不一定会谈论Scala类型系统,因为这里的想法适用于许多不同的语言 .

    首先,在谈论 Map 之前,让我展示如何 Build 更深层次的并行层次结构 . 这很简单,只需插入另一个级别:

    FactoryBase
       Factory[+F <: Factory[F,P], +P <: Product[F,P]] <: FactoryBase
          SomeFactoryGroup[+F <: SomeFactoryGroup[F,P], +P <: SomeProductGroup[F,P]] <: Factory[SomeFactoryGroup, SomeProductGroup]
            FooFactory <: SomeFactoryGroup[FooFactory, FooProduct]
    ProductBase
       Product[+F <: Factory[F,P], +P <: Product[F,P]] <: ProductBase
          SomeFactoryGroup[+F <: SomeFactoryGroup[F,P], +P <: SomeProductGroup[F,P]] <: Product[SomeFactoryGroup, SomeProductGroup]
            FooProduct <: SomeProductGroup[FooFactory, FooProduct]
    

    您可以根据需要插入任意数量的级别 . 我们需要所有这些,包括非泛型 FactoryBaseProductBase . 虽然它们并非严格必要,因为它们可以通过存在类型来逃避,但这些可能会变得笨拙 . 比 (SomeFactory | exist F <: Factory[F,P], exist P <: Product[F,P], SomeFactory <: F) 更容易说 FactoryBase (这不是Scala语法,故意) . 有时存在是非常有用的,但不是在这个例子中(也许你可以在系统的其他部分使用它们) . 无论如何,这些 FactoryBase 对象将被放置在 Iterable[FactoryBase] 中以管理 生产环境 运行 .

    现在来到 Map . 有 生产环境 运行,在此期间每个工厂可以 生产环境 一个产品实例(或者可能没有) . 在工厂和 生产环境 运行期间,我们需要找到该工厂 生产环境 的产品 .

    您尝试过一种解决方案:将 生产环境 运行表示为(或包含或其他)从工厂到产品的映射 . 在伪代码中暂时忽略类型:

    productionrun.lookup(factory) = productionrun.mapFromFactoryToProduct.lookup(factory)
    

    如果我们坚持 FactoryBaseProductBase ,这甚至可能有用 . 但这种方法是有限的 . Map 将一堆类型A的东西映射到一堆(可能是不同的)类型B的东西 . 所有键都是类型相同的,并且所有值都是类型相同的 . 这显然不是我们所拥有的 . 所有工厂都有不同类型,所有产品也是如此 . 我们可以通过忘记部分类型信息来解决这个问题,也就是说,通过将键和值减少到最小公分母类型 . 但是如果我们稍后需要恢复这种类型的信息呢?这是不可能静止的,它被遗忘,永远失去 .

    另一个解决方案与第一个解决方案表面对称:工厂代表(或包含,在这种情况下)从 生产环境 运行到产品的映射 .

    factory.lookup(productionrun) = factory.mapFromProductionRunToProduct.lookup(productionrun)
    

    在这种情况下, 生产环境 运行仅由其唯一ID表示 . 但在类型级别,这种解决方案与第一种解决方案截然不同,而且要好得多 . 每个映射中的所有键都是类型相同的(实际上,它们在不同的工厂中都是类型相同的) . 每个映射中的所有值都是类型相同的(此类型特定于工厂) . 在任何阶段都没有丢失类型信息 .

    所以,总结一下 . 有一个代表 生产环境 运行的类 PR . 创建一个方法 Factory[F,P].findProduct(pr:PR)->Product[F,P] ,通过 Map[PR, Product[F,P]] 实现 . 让 makeProduct 方法接受 PR 值,并将生成的产品添加到 Map 中,并按 PR 值键入 . 如果需要,可以将其包装在方法 PR.lookUpProductByFactory[F,P](f:Factory[F,P])->Product[F,P] 或某些方法中 . 另外,将其包装在 FactoryBase.findProduct(pr:PR)->ProductBase 中,并将其包装到 PR.lookUpProductBaseByFactoryBase(f:FactoryBase)->ProductBase 中 .

    而已 . 我希望这两种解决方案能满足您的需求 .

相关问题