我正在尝试将 java webapp 转换为 scala。我一直在努力解决类型不匹配错误,这对于确定问题原因没有太大帮助,或者我不知道如何阅读它们。我曾尝试在下面的帖子中询问整个设计中的问题,但看起来这对我没有帮助。
https://stackoverflow.com/questions/32573360/scala-type-mismatch-errors-when-using-type-classes-and-factory-methods
所以我将我的问题分解成小问题。希望这将带来最终的解决方案。有人可以帮我解决 DataContext 和 DataContextBuilder 之间的契约吗?
摘自上述帖子的部分内容:
trait DBObject
trait AggDBObject extends DBObject
trait RawDBObject extends DBObject
class Dimensions1Agg extends AggDBObject
class Dimensions2Agg extends AggDBObject
class Dimensions1Raw extends RawDBObject
trait IDataContext[A <: DBObject] {
var XsummaryData: A = _
var XByDateData : Map[String, A] = _
... more feilds ..
def restrictAccess = {.. some impl ..}
}
trait IDataContextBuilder[A <: DBObject] {
def initDataPoints(dataContext: IDataContext[A]): Unit
}
class Dimension1AggContextBuilder extends IDataContextBuilder[Dimension1Agg] {
.. override method impl..
}
object IDataContext {
def apply(sId: Int, mId: Int): DataContext[_ <: DBObject] = {
(sId, mId) match {
case (1, 1) => {
new DataContext[Dimension1Agg]()
}
case (1, 2) => {
new DataContext[Dimension1Raw]()
}
}
}
}
我正在创建的对象的 apply 方法的返回类型是否正确?如果我删除它,那么我会在调用站点获得一些复杂的返回类型“val dataContext: DataContext[_ >: Dimension1Agg with Dimension1Raw <: DBObject] with Product with Serialized { .. }”
object IDataContextBuilder {
def apply(sId: Int, daId: Int): IDataContextBuilder[_ <: DBObject] = {
(sId, daId) match {
case (1, 1) => {
new Dimension1AggContextBuilder
}
case (1, 2) => {
new Dimension1RawContextBuilder
}
}
}
}
您是否发现上述工厂方法存在任何问题?
将该 DataContext 传递给 DataContextBuilder 时出现以下错误
类型不匹配;找到:IDataContext[_$1],其中类型 $1 <:需要 DBObject:IDataContext[$19]
上面的 _$1 和 _$19 是因为 scalac 无法确定类型吗?
编辑:
将其称为 DataService 类,用于接收用户请求参数、编排上述所有组件并撰写响应。我知道它不起作用,但我正在尝试进行增量重构。 DataService 检查请求参数,将该信息传递给工厂方法以创建多个 DataContext、DataContextBuilder 和 DataWorker;调用所有DataContextBuilder的initDataPoints方法,等待它们完成;调用所有Dataworker的generateView方法,等待它们完成,最后撰写响应。
初始化每个度量到 DataContext 的映射 varmeasureToDCMap = MapInt, IDataContext[_ <: DBObject]
对于每个测量器,它执行以下操作来填充 DataContext
从其工厂方法中获取具体的 DataContext 从其工厂方法获取具体的 DataContextBuilder 调用DataContextBuilder的initDataPoints方法
此时,所有 DataContext 均已填充相应的 DimensionData。
为每个指标的每个度量的每个 View 初始化 dataView map (每天的平均推文、每个主题的平均推文、推文率百分位等)
var dataView = MapInt, Map[Int, Map[Int, List[DataView]]] 5) 对于每个 View ,它执行以下操作以生成最终的可见响应
Get concrete ViewWorker . i.e. Metrix1ViewWorker or MetrixViewWorker
Call getData method
- 生成最终响应
最佳答案
这里有一种稍微简化的方法。我拿出了一些额外的接口(interface)等:
trait DBObject
trait AggDBObject extends DBObject
trait RawDBObject extends DBObject
class Dimensions1Agg extends AggDBObject
class Dimensions2Agg extends AggDBObject
class Dimensions1Raw extends RawDBObject
class DataContext[A <: DBObject] {
var XsummaryData: A = _
var XByDateData : Map[String, A] = _
def restrictAccess = {
// .. some impl ..
}
}
object DataContext {
def buildDim1Agg() : DataContext[Dimensions1Agg] = {
// Put the custom code for Dim1 you had at `initDataPoints`
// ...
new DataContext[Dimensions1Agg]()
}
// Add other builder methods if you have lots of custom code, or functions shared across builders.
/** This is your main builder function.
* Note that per your request, it is not polymorphic, but tied to a specific type of DBObject. */
def apply(sId: Int, mId: Int): DataContext[_ <: DBObject] = {
(sId, mId) match {
case (1, 1) => buildDim1Agg()
case (1, 2) => buildDim1Agg()
//... add more cases and a default case.
}
}
}
您没有发布调用所有这些的代码,并且缺少一些上下文,因此我做了很多假设。
特别是,如果您愿意在这里进行实际的 OOP,您可以将每个 DBObject 类的自定义代码移动到该特定类的构造函数中(每个类现在如何初始化自身),并拥有一个更通用的 DataContext,它不关心它存储的具体类型:
trait DBObject
trait AggDBObject extends DBObject
trait RawDBObject extends DBObject
class Dimensions1Agg extends AggDBObject {
// Put the custom code you had at `initDataPoints`
}
class Dimensions2Agg extends AggDBObject {
// Put the custom code you had at `initDataPoints`
}
class Dimensions1Raw extends RawDBObject {
// Put the custom code you had at `initDataPoints`
}
class DataContext(var summaryData: DBObject) {
var XByDateData : Map[String, DBObject] = _
def restrictAccess = {
// .. some impl ..
}
}
object DataContext {
/** This is your main builder function.
* This version IS polymorphic, your data context holds a DBObject of the right type, but callers don't need to know which one. */
def apply(sId: Int, mId: Int): DataContext = {
(sId, mId) match {
case (1, 1) => new DataContext(new Dimensions1Agg())
case (1, 2) => new DataContext(new Dimensions2Agg())
//... add more cases and a default case.
}
}
}
希望有帮助。
关于java - 类型类的工厂方法需要 Scala 设计建议,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32727084/