考虑以下 Scala 代码
def NOTImplementedIn[T<: AnyRef](t:T):String =
throw new Exception(t.getClass.getName+": Input type not implemented")
def NOTImplementedOut[T<: AnyRef](s:String):T =
throw new Exception("Output type not implemented")
Run Code Online (Sandbox Code Playgroud)
在第一种情况下,可以推断输入类型T。有没有办法推断T第二种情况下的输出类型?我想在异常中包含类型名称。
的下限类型T总是会被推断出来。如果没有明确给出类型下限,则没有任何限制。
这个推论是正确的。作为通用的参数类型将被删除运行时间,来电f[String]("foo"),f[File]("foo")并f[List[Int]]("foo")和f[Nothing]("foo")都是同一个呼叫f("foo")在运行时。其结果必须是有效的String,File,List[Int],和Nothing。存在Nothing是满足这一点的唯一方式(这意味着不会有结果)。如果有更强的下限类型,它只需要是那种类型。
这方面的一个后果是,一个常规的f[T >: LowerBound](parameters): T,地方T不出现在parameters,没有不同f(parameters): LowerBound,并没有作出任何一点上,通用(当LowerBound没有明确指出,绑定的类型Nothing)
与调用f[T]("foo")仅f("foo")在运行时相同的想法,很明显,异常消息中不能出现的名称。所以T必须在运行时以某种方式传递,正如 Dave 所指出的那样,清单是做到这一点的正常方式。现在,T确实出现在参数中,一切都不一样了。如果调用f[File]("foo"),编译器会将其转换为f("foo")(manifest_of_file)wheremanifest_of_file允许获取类型文件的名称。
这仍然不是推理,正如File已经明确写的那样。T推断类型的唯一方法是从f上下文中调用的预期类型。如果这样做val file: File = f("foo"),或者将f("foo")File 类型的参数的值传递给某个例程,或者简单地写入f("foo"): File,是否会File被推断?不,不会。再次,Nothing将被推断。原因是它是比 File 更精确的类型,并且由于编译器可以传递 Manifest[File] 或 Manifest[Nothing](以及两者之间的任何类型),它会选择更精确的类型。对于Nothing,这可能令人惊讶,但假设您的类型下限是String,而预期类型只是AnyRef,则没有明显的理由选择 AnyRef 而不是更好的 String。
def f[T >: String : Manifest](s: String): T = "type is " + manifest[T].toString
val x: AnyRef = f("foo"): AnyRef
x: Anyref = type is java.lang.String
Run Code Online (Sandbox Code Playgroud)
与您的示例和Nothing下限类型相同:
def g[T: Manifest](s: String): T = throw new Exception("type is " + manifest[T])
val y: String = g("foo"): String
java.lang.Exception: type is Nothing
at .g(<console>:7)
....
Run Code Online (Sandbox Code Playgroud)