请查看以下编译良好的代码:
trait ParserError
trait Parser[+A]{
def run[A](input:A):Either[ParserError, A]
}
object Parser{
case object ParserErrorImpl extends ParserError
def string(s:String):Parser[String] = new Parser[String]{
def run[String](input:String) = {
if(input == s) Right(input) else Left(ParserErrorImpl)
}
}
}
object Runner{
import Parser._
def main(args:Array[String]):Unit={
println(string("aaa").run("aaa"))
}
}
当我将Right(input)
更改为Right(s)
时,会出现编译错误:
Error:(13, 58) type mismatch;
found : s.type (with underlying type String)
required: String
def run[String](input:String) = if(input == s) Right(s) else Left(ParserErrorImpl)
^
你能解释为什么会发生这种情况,因为我不完全清楚这一点吗?
答案 0 :(得分:3)
您正在通过(重新)在run方法中定义类型参数A来隐藏它。使特征Parser按类型A参数化,并将该类型用作run方法的参数类型。
这是固定版本:
trait ParserError
trait Parser[A]{
def run(input:A):Either[ParserError, A] // <-- not run[A] !!!
}
object Parser{
case object ParserErrorImpl extends ParserError
def string(s:String):Parser[String] = new Parser[String]{
def run(input:String) = {
if(input == s) Right(input) else Left(ParserErrorImpl)
}
}
}
原因是您无法将已实现方法的类型设置为String。你想这样做,但实际发生的是&#34; String&#34;只是方法类型的另一个标识符;它也可能是&#34; Strings&#34;或&#34; Stringozoid&#34;。请参阅@Tzach Zohar的回答,以获得更丰富的解释。
另外,我删除了协方差以使事情更简单,但是你可能想要保留它,所以你需要做一个好的旧的下界技巧。请查看@Leif Ericson的答案以获得完整的解决方案。
答案 1 :(得分:3)
当覆盖类型参数化方法时,不能使用String
将类型“设置”为def methodName[String]
- 无论你“传递”什么,因为类型参数实际上不是解释为参数 value ,而是作为新参数名称 。在这种情况下,在:
def string(s:String):Parser[String] = new Parser[String]{
def run[String](input:String) = {
if(input == s) Right(input) else Left(ParserErrorImpl)
}
}
当您撰写def run[String]
时,您正在创建新类型参数名为“字符串”,而不是使用{{1}的重写方法}。现在,scala.Predef.String
保证具有该类型(无论它是什么,可以是Int!),但input
不是。
要看到这一点 - 尝试在原始编译版本中使用不同的名称而不是“String” - 并看到它仍然可以编译:
s
要解决此问题 - 在这种情况下,您可以删除方法的类型参数并使用@slouc建议的类的参数 - 您将它们命名为def string(s:String):Parser[String] = new Parser[String]{
def run[SomeUnknownType](input:SomeUnknownType) = {
if(input == s) Right(input) else Left(ParserErrorImpl)
}
}
,但这是两个不同的参数。
答案 2 :(得分:2)
你实际上可以保持协方差,同时仍然编译代码:
trait ParserError
trait Parser[+A] {
def run[B >: A](input: B): Either[ParserError, B]
}
object Parser {
case object ParserErrorImpl extends ParserError
def string(s: String): Parser[String] = new Parser[String] {
def run[B >: String](input: B) = {
if (input == s) Right(s) else Left(ParserErrorImpl)
}
}
}
object Runner {
import Parser._
def main(args: Array[String]): Unit = {
println(string("aaa").run("aaa"))
}
}