我很困惑如何在Scala中解决这个问题。在Java中,我放弃了泛型或使用强制转换,但Scala比这更严格。这就是我所拥有的。
Factory[+F <: Factory[F]]
和一些与此无关的具体实现。这是协变的。这个类有一个创建产品的方法 - 见下一点。Product[+F <: Factory[F]]
。这些也是协变和只读的。任何特定的工厂只生产一种产品(即没有多种不同的子类型--FooFactory生产FooProduct,BarFactory生产BarProduct等)。Iterable[Factory[_]]
。这是麻烦的第一部分。 Scala似乎将_
理解为Any
,忽略了类型本身的约束F <: Factory[F]
。Map[Factory[_],Product[_]]
并且还尝试了Map[Factory[_],Product[Factory[_]]]
。这是我遇到麻烦的另一部分。第一个_
与第二个_
取消关联,两者似乎都隐含为Any
。我不能做的是创建该地图。它可能或不重要,但我通过使用另一种看起来像语法结构的方法来做(并且必须这样做)。它来自一个特点:
gather[B](fun: A => B): Map[A,B]
,而不是在特征本身中实现并且通常是类型化的)来获取工厂集合。实际的工厂类型是通用的和未知的。 Map
醇>
编译器的问题不在于'gather'方法,而是在调用它的代码中。我无法将结果投射到Map[Factory[_],Product[_]]
,因为_在任何一个地方都不符合F <: Factory[F]
...我很乐意为_
使用基类型,但是那将是Factory[Factory[Factory[Factory......(infinitely many times)]]]]]]....
,我不知道该怎么做。
请帮忙!
学习者
答案 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)
目前尚不清楚为什么Factory
或Product
需要是泛型,很多协方差。无论是否实际需要,有一个简单的非泛型类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]
您可以根据需要插入任意数量的关卡。我们需要所有这些内容,包括非通用FactoryBase
和ProductBase
。虽然它们不是严格必要的,因为它可以逃避存在类型,但这些可能变得笨拙。说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)
如果我们坚持FactoryBase
和ProductBase
,这甚至可能有用。但这种方法是有限的。地图将一堆类型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
。
就是这样。我希望这两种解决方案能够满足您的需求。