为什么`Some(123).isInstanceOf [Option [List [String]]]`* not *给出未经检查的警告?

时间:2012-07-19 05:14:38

标签: scala warnings compiler-warnings type-erasure unchecked

使用.isInstanceOf[GenericType[SomeOtherType]]时,GenericTypeSomeOtherType是任意类型(合适的类型),Scala编译器会因类型擦除而提供未经检查的警告:

scala> Some(123).isInstanceOf[Option[Int]]
<console>:8: warning: non variable type-argument Int in type Option[Int] is unchecked since it is eliminated by erasure
              Some(123).isInstanceOf[Option[Int]]
                                    ^
res0: Boolean = true

scala> Some(123).isInstanceOf[Option[String]]
<console>:8: warning: non variable type-argument String in type Option[String] is unchecked since it is eliminated by erasure
              Some(123).isInstanceOf[Option[String]]
                                    ^
res1: Boolean = true

但是,如果SomeOtherType本身是通用类型(例如List[String]),则不会发出警告:

scala> Some(123).isInstanceOf[Option[List[String]]]
res2: Boolean = true

scala> Some(123).isInstanceOf[Option[Option[Int]]]
res3: Boolean = true

scala> Some(123).isInstanceOf[Option[List[Int => String]]]
res4: Boolean = true

scala> Some(123).isInstanceOf[Option[(String, Double)]]
res5: Boolean = true

scala> Some(123).isInstanceOf[Option[String => Double]]
res6: Boolean = true

(回想一下,元组和=>Tuple2[]Function2[]泛型类型的语法糖

为什么没有发出警告? (所有这些都在Scala REPL 2.9.1中,带有-unchecked选项。)

1 个答案:

答案 0 :(得分:19)

我查看了Scala编译器源代码,并发现了一些有趣的内容

scala.tools.nsc.typechecker.Infer

这是您发现警告的地方。如果你仔细看第1399行:

def checkCheckable(pos: Position, tp: Type, kind: String)

是生成警告的位置,您会看到一些嵌套方法,包括检查方法:

 def check(tp: Type, bound: List[Symbol]) {
        def isLocalBinding(sym: Symbol) =
          sym.isAbstractType &&
          ((bound contains sym) ||
           sym.name == tpnme.WILDCARD || {
            val e = context.scope.lookupEntry(sym.name)
            (e ne null) && e.sym == sym && !e.sym.isTypeParameterOrSkolem && e.owner == context.scope
          })
        tp match {
          case SingleType(pre, _) =>
            check(pre, bound)
          case TypeRef(pre, sym, args) =>
            if (sym.isAbstractType) {
              if (!isLocalBinding(sym)) patternWarning(tp, "abstract type ")
            } else if (sym.isAliasType) {
              check(tp.normalize, bound)
            } else if (sym == NothingClass || sym == NullClass || sym == AnyValClass) {
              error(pos, "type "+tp+" cannot be used in a type pattern or isInstanceOf test")
            } else {
              for (arg <- args) {
                if (sym == ArrayClass) check(arg, bound)
                else if (arg.typeArgs.nonEmpty) ()   // avoid spurious warnings with higher-kinded types
                else arg match {
                  case TypeRef(_, sym, _) if isLocalBinding(sym) =>
                    ;
                  case _ =>
                    patternWarning(arg, "non variable type-argument ")
                }
              }
            }
            check(pre, bound)
          case RefinedType(parents, decls) =>
            if (decls.isEmpty) for (p <- parents) check(p, bound)
            else patternWarning(tp, "refinement ")
          case ExistentialType(quantified, tp1) =>
            check(tp1, bound ::: quantified)
          case ThisType(_) =>
            ;
          case NoPrefix =>
            ;
          case _ =>
            patternWarning(tp, "type ")
        }
  }

虽然我不是Scala编译器的专家,但我们都应该感谢这些人让代码变得如此不言自明。让我们看一下tp match块和处理过的案例:

  • 如果是单一类型
  • 如果是型号ref
    • 如果是抽象类型
    • If是别名类型
    • 如果是Null,Nothing或Anyval
    • 所有其他情况

如果您查看所有其他情况,有一行也会被评论:

else if (arg.typeArgs.nonEmpty) ()   // avoid spurious warnings with higher-kinded types

它告诉您如果您的类型具有其他类型参数(如Function2或Tuple2)会发生什么。检查功能返回单元而不执行任何测试。

我不是出于这个原因这样做了,但是你可能想要打开一个bug https://issues.scala-lang.org/browse/SI 提供您在此处发布的代码作为一个优秀的测试用例,并参考我上面复制的Infer.scala源代码。