def test[T: ClassTag]: T = {
println(classTag[T])
null.asInstanceOf[T]
}
val x1: Int = test
val x2: Int = test[Int]
prints
Nothing
Int
I would expect the compiler to guess the Int
type without the need to provide it explicitly (EDIT: on the right hand side, i.e. make val x1: Int = test
work).
Is there perhaps a workaround to get rid of the explicit type annotation?
CodePudding user response:
I suspect that compiler can't infer Int
in val x1: Int = test[???]
because both options:
val x1: Int = test[Int] // returns Int
and
val x1: Int = test[Nothing] // returns Nothing <: Int
are valid. So compiler just has to guess what option you meant.
When compiler has a choice it often selects the minimal type out of the options. And currently this is Nothing
.
Why Scala Infer the Bottom Type when the type parameter is not specified?
In principle, if you'd like to explore the type of left hand side you can make test
a macro. Then it can be even not generic. Making it whitebox means that it can return a type more precise than declared (Any
) e.g. Int
.
import scala.reflect.macros.whitebox // libraryDependencies = scalaOrganization.value % "scala-reflect" % scalaVersion.value
import scala.language.experimental.macros
def test: Any = macro testImpl
def testImpl(c: whitebox.Context): c.Tree = {
import c.universe._
val T = c.internal.enclosingOwner.typeSignature
println(s"T=$T")
q"""
_root_.scala.Predef.println(_root_.scala.reflect.runtime.universe.typeOf[$T])
null.asInstanceOf[$T]
"""
}
// in a different subproject
val x1: Int = test // prints at runtime: Int
// scalacOptions = "-Ymacro-debug-lite"
//scalac: T=Int
//scalac: {
// _root_.scala.Predef.println(_root_.scala.reflect.runtime.universe.typeOf[Int]);
// null.asInstanceOf[Int]
//}
CodePudding user response:
Actually there's nothing wrong with type inference here, type inference here means that the compiler should figure out that the T
type parameter is Int
, and the returned value from your method expression is an integer, and that's working properly:
x1: Int = 0 // this is the result of your first line
You can also try printing this:
println(x1.getClass) // int
What's not working as expected is implicit resolution for a generic type "ClassTag[_]"
. The reason it prints Nothing
is that the compiler found the object scala.reflect.ClassTag.Nothing : ClassTag[Nothing]
suitable for your case. Now about this new thing, there are loads of content on SO and the internet for why this happens and how to deal with it in different cases.
Here's another piece of code to differentiate type inference
with type erasure
in your case:
def nullAs[T]: T = null.asInstanceOf[T]
val int: Int = nullAs // 0: Int
// type erasure:
case class Container[T](value: T)
implicit val charContainer: Container[Char] = Container('c')
def nullWithContainer[T](implicit container: Container[T]): T = {
println(container)
null.asInstanceOf[T]
}
val long: Long = nullWithContainer
// prints Container(c)
// 0L
Which means type inference is done correctly, but type erasure has happened, because in runtime, the type of charContainer
is Container
, not Container[Char]
.
CodePudding user response:
val x1: Int = test
In this line, the Int
you have given is the type for the value x1
which would hold the result of the function test
and as for the classTag
of T
the compiler finds no information which is why it returns Nothing
, in which case its taking val x1: Int = test[Nothing]
so you would always have to mention a Type for test
else it would print Nothing