我们能否推断出通用输出类型?

时间:2011-10-11 20:29:18

标签: scala

考虑以下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")

在第一种情况下,可以推断输入类型T。有没有办法在第二种情况下推断输出类型T?我想在例外中包含类型名称。

2 个答案:

答案 0 :(得分:4)

始终会推断T的下限类型。如果没有明确给出下限类型绑定,则Nothing是绑定。

这个推论是正确的。由于通用参数类型会被删除为运行时,因此在运行时对f[String]("foo")f[File]("foo")f[List[Int]]("foo")以及f[Nothing]("foo")的调用是相同的调用f("foo")。结果必须是有效的StringFileList[Int]Nothing。成为Nothing是满足它的唯一方法(这意味着没有结果)。如果有一个更强的低级类型绑定,它只需要是那种类型。

这样做的结果是,f[T >: LowerBound](parameters): T,中未显示T的例行parametersf(parameters): LowerBound没有区别,并且没有任何意义它是通用的(当LowerBound没有明确说明时,绑定是类型Nothing

与运行时调用f[T]("foo")仅为f("foo")的想法相同,很明显,该名称不会出现在异常消息中。因此,T必须在运行时通过,并且清单是正常的方法,如Dave所述。现在,T确实出现在参数中,并且一切都不同。如果有人调用f[File]("foo"),编译器会将其转换为f("foo")(manifest_of_file),其中manifest_of_file允许获取类型File的名称。

这仍然没有推论,因为File已被明确写出。可以推断类型T的唯一方法是从上下文中对f的预期调用类型。如果一个人val file: File = f("foo"),或者将f("foo")类型的参数值传递给某个例程,或者只是写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

与您的示例相同,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)
        ....

答案 1 :(得分:3)

是的,使用Manifest

def NOTImplementedOut[T<: AnyRef](s:String)(implicit m: scala.reflect.Manifest[T]):T =
  throw new Exception(m.toString + ": Output type not implemented")