我正在尝试实现类似于userFor
的Scala函数,如下面的代码片段所示,其中函数的返回类型取决于输入参数的类型。在此示例中,Scala编译器报告“类型System1User.type
的表达式不符合预期类型SystemUser[S]
”。
有没有办法创建一个工厂方法,例如userFor
,它将使用输入的编译时类型来返回正确的输出类型?
sealed trait System {
def name: String
}
case object System1 extends System { def name = "1" }
case object System2 extends System { def name = "2" }
sealed trait SystemUser[S <: System] {
def use(s: S): String
}
object System1User extends SystemUser[System1.type] {
override def use(s: System1.type) = s"${s.name} is in use"
}
object System2User extends SystemUser[System2.type] {
override def use(s: System2.type) = s"${s.name} is in use"
}
object SystemUser {
//TODO: how to make the types work out here?
def userFor[S <: System](sys: S): SystemUser[S] = {
sys match {
case System1 => System1User
case System2 => System2User
}
}
}
答案 0 :(得分:2)
问题是您承诺从SystemUser[S]
返回userFor
,其中S
是System
的某个子类型。但你不能遵守这个承诺。你没有回来SystemUser[S]
;您将返回SystemUser[System1.type]
或SystemUser[System2.type]
。如果S
不是这两种类型中的一种,会发生什么?如果某人使用userFor
参与调用SomeNewSystemSubtype
(让我们说它是System
的有效子类型)会怎样?
您可以删除依赖关系以键入S
,然后只返回&#34;某些子类型System
&#34;:
def userFor[S <: System](sys: S): SystemUser[_ <: System] = {
更清洁的解决方案是在其类型参数中设置SystemUser
协变,因为SystemUser[System1.type]
将是SystemUser[System]
的子类,这意味着您将能够执行此操作:
sealed trait SystemUser[+S <: System] {
def use(s: S): String
}
...
def userFor[S <: System](sys: S): SystemUser[System] = {
但遗憾的是,您的方法def use(s: S): String
的参数类型为S
,而且这是一个逆变位置。我不确定你是否熟悉这些东西,但是要注意这一点并不容易 - 你需要将你的方法修改为def use[T >: S](s: T): String
之类的东西,这需要改变实现(因为现在任何<{1}}以上的类型可以显示为参数),在您的情况下,这可能不值得。
答案 1 :(得分:0)
我决定在我的情况下,这样就足够了:
object SystemUser {
def userFor(sys: System1.type): SystemUser[System1.type] = {
sys match {
case System1 => System1User
}
}
def userFor(sys: System2.type): SystemUser[System2.type] = {
sys match {
case System2 => System2User
}
}
}