Scala中的泛型集合有问题

时间:2011-06-15 15:07:52

标签: generics scala collections scala-collections

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

  1. 抽象基类Factory[+F <: Factory[F]]和一些与此无关的具体实现。这是协变的。这个类有一个创建产品的方法 - 见下一点。
  2. 表示工厂产品的抽象基类Product[+F <: Factory[F]]。这些也是协变和只读的。任何特定的工厂只生产一种产品(即没有多种不同的子类型--FooFactory生产FooProduct,BarFactory生产BarProduct等)。
  3. 我必须有一个包含具体实例的工厂集合(Capt。明显在这里说)。目前这是Iterable[Factory[_]]。这是麻烦的第一部分。 Scala似乎将_理解为Any,忽略了类型本身的约束F <: Factory[F]
  4. 我有一个聚合工厂方法,要求每个工厂生产相同参数的产品。结果是工厂到他们的产品的地图,目前编码为Map[Factory[_],Product[_]]并且还尝试了Map[Factory[_],Product[Factory[_]]]。这是我遇到麻烦的另一部分。第一个_与第二个_取消关联,两者似乎都隐含为Any
  5. 我不能做的是创建该地图。它可能或不重要,但我通过使用另一种看起来像语法结构的方法来做(并且必须这样做)。它来自一个特点:

    1. 调用一个声明的方法(在此处称之为gather[B](fun: A => B): Map[A,B],而不是在特征本身中实现并且通常是类型化的)来获取工厂集合。实际的工厂类型是通用的和未知的。
    2. 迭代该泛型集合并执行传递给它的函数。该函数实际上是调用工厂方法。
    3. 返回F到P
    4. 的不可变Map

      编译器的问题不在于'gather'方法,而是在调用它的代码中。我无法将结果投射到Map[Factory[_],Product[_]],因为_在任何一个地方都不符合F <: Factory[F] ...我很乐意为_使用基类型,但是那将是Factory[Factory[Factory[Factory......(infinitely many times)]]]]]]....,我不知道该怎么做。

      请帮忙!

      学习者

4 个答案:

答案 0 :(得分:5)

大多数情况下,在类型中使用_是不对的,除非你知道自己在做什么。如果你不理解存在类型,那么你肯定不知道你在做什么。并不是Scala认为_Any,而是知道 _可以代表任何事情 - 不管是Any,{ {1}}或介于两者之间的任何内容。

总结一下,Nothing表示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] }

答案 1 :(得分:2)

如果我的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]

随意省略您不需要的任何类型参数。

还不清楚为什么需要将工厂映射到产品的地图。地图通常会将 names 等内容映射到实体。也就是说,给定名称,找到具有该名称的现有实体。工厂创造新的凭空。无论如何,如果您确实需要这样的地图,可以使用Map[FactoryBase, ProductBase]。这并不能保证FooFactory映射到FooProduct而不是BarProduct,但Map无法提供。Product。然后,您可以将Factory放在其各自的Map内。您已经知道编译时的映射,不需要运行时n。在{{1}}手上,产品生产后不需要与任何工厂相关联。

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

答案 2 :(得分:1)

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

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

答案 3 :(得分:1)

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

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

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

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。虽然它们不是严格必要的,因为它可以逃避存在类型,但这些可能变得笨拙。说FactoryBase(SomeFactory | exist F <: Factory[F,P], exist P <: Product[F,P], SomeFactory <: F)更容易(这不是Scala语法,故意)。有时存在是非常有用的,但不是在这个例子中(也许你可以在系统的其他部分使用它们)。无论如何,这些FactoryBase对象将被放置在Iterable[FactoryBase]中以管理生产运行。

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

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

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

如果我们坚持FactoryBaseProductBase,这甚至可能有用。但这种方法是有限的。地图将一堆类型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值,并将生成的产品添加到地图中,并以PR值为键。如果需要,可以将其包装在方法PR.lookUpProductByFactory[F,P](f:Factory[F,P])->Product[F,P]或其他方法中。另外,将其包装在FactoryBase.findProduct(pr:PR)->ProductBase中,然后将 包装到PR.lookUpProductBaseByFactoryBase(f:FactoryBase)->ProductBase

就是这样。我希望这两种解决方案能够满足您的需求。