当前位置: 动力学知识库 > 问答 > 编程问答 >

Scala generic type inference inconsistent with ClassTag

问题描述:

I'm using Scala 2.10, and have found what I consider weird behaviour when using type inference for generic parameters. Consider the example below (note i is just a dummy variable):

scala> import scala.reflect.{ClassTag,classTag}

|

| def broken[T : ClassTag](i : Int) : List[T] = {

| println("ClassTag is : " + classTag[T])

| List.empty[T]

| }

import scala.reflect.{ClassTag, classTag}

broken: [T](i: Int)(implicit evidence$1: scala.reflect.ClassTag[T])List[T]

scala> broken(3)

ClassTag is : Nothing

res0: List[Nothing] = List()

scala> val brokenVal : List[String] = broken(3)

ClassTag is : Nothing

brokenVal: List[String] = List()

The call broken(3) seems consistent, in that the print statement inside the function agrees with the inferred ClassTag.

However, the second statement, there is an inconsistency between what the compiler infers for the ClassTag, and is printed inside the function, and what the actual return type is.

I would have expected the compiler to infer the ClassTag from the type declaration i.e. List[String]. Is this not correct? Can somebody enlighten me?

Some further context here: I'm actually using the classtag to filter a collection in some code (not shown here), and it's obviously failing when I don't specify T explicitly, as it's comparing against type Nothing.

网友答案:

The actual return type for your second example is List[Nothing], so the ClassTag is perfectly consistent with it. This happens because the List is covariant (defined as List[+A] instead of just List[A]). This covariance allows you to write statements like list = Nil (because Nil.type = List[Nothing]), but it also allows compiler to infer bottom type as list type argument, because List[Nothing] is subtype of List[String], again, due to it's covariance. So the actual type mismatch is between the return type and the type of brokenVal.

To overcome this limitation, you might use a invariant type as your return type, i.e.:

import scala.reflect.ClassTag

class Invariant[T]

def f[T: ClassTag](i: Int): Invariant[T] = {
  println("ClassTag: " + implicitly[ClassTag[T]])
  new Invariant[T]
}

val ret: Invariant[String] = f(3)
// ClassTag: java.lang.String
// ret: Invariant[String] = [email protected]

On other side, you might let compiler to infer T from something else, for example, by passing a value of type T as argument. You cannot make this example work on vanilla Lists, because you cannot change variance of them.

分享给朋友:
您可能感兴趣的文章:
随机阅读: