Consider the following Scala code
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 Excepti开发者_如何学Con("Output type not implemented")
In the first case, it is possible to infer the input type T
. Is there any way to infer the output type T
in the second case? I would like to include the type name in the exception.
The lower type bound of T
will always be inferred. If no lower type bound is explicitly given, Nothing is the bound.
This inference is the correct one. As generic parameter type are erased as run time, calls to f[String]("foo")
, f[File]("foo")
and f[List[Int]]("foo")
and f[Nothing]("foo")
are the same call f("foo")
at run time. The result must be a valid String
, File
, List[Int]
, and Nothing
. Being Nothing
is the only way to satisfy that (which means there will be no result). If there was a stronger lower type bound, it would only need to be of that type.
A consequence of that is that a routine f[T >: LowerBound](parameters): T,
where T
does not appears in the parameters
, is no different from f(parameters): LowerBound
, and there is no point in making it generic (when LowerBound
is not explicitly stated, the bound is type Nothing
)
With the same idea that the call f[T]("foo")
is only f("foo")
at runtime, it is clear that the name of cannot appear in the exception message. So T
has to be someway passed at runtime, and manifests are the normal way to do that, as noted by Dave. Now, T
does appear in the parameters, and everything is different. if one calls f[File]("foo")
, the compiler will transform that to f("foo")(manifest_of_file)
where manifest_of_file
allows among other thing to get the name of the type File.
This is still not inference, as File
has been explicitly written. The only way type T
can be inferred is from the expected type of the call to f
in context. If one does val file: File = f("foo")
, or passes f("foo")
the value of a parameter of type File to some routine, or simply writes f("foo"): File
, will File
be inferred? No, it won't. Once again, Nothing
will be inferred. The reason is that it is a more precise type than File, and as the compiler can pass either a Manifest[File] or a Manifest[Nothing] (and any type in between) it choose the more precise one. This may be surprising for Nothing
, but suppose that your lower type bound is String
, and the expected type is simply AnyRef
, there is no obvious reason to chose AnyRef rather than the better 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
Same with your example and Nothing
as the lower type bound:
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)
....
Yes, using a Manifest
.
def NOTImplementedOut[T<: AnyRef](s:String)(implicit m: scala.reflect.Manifest[T]):T =
throw new Exception(m.toString + ": Output type not implemented")
精彩评论