Scala类型在编译器和运行时之间存在分歧吗?

时间:2014-09-16 00:19:33

标签: scala scala-2.10

java.util.ArrayList转换为scala.collection.immutable.List,2.10编译器和运行时似乎存在分歧,关于val emits的类型:

import org.ahocorasick.trie._
import scala.collection.JavaConverters._    // convert Java colllections to Scala ones

object wierd {

  val trie = new Trie

  def trieInit(patterns: List[String]) {
    trie.onlyWholeWords();
    for (pattern <- patterns)
      trie.addKeyword(pattern)
  }

  def patternTest(text : String) : List[String] = 
  {
    val emitsJ = trie.parseText(text)
    val emits = emitsJ.asScala map (i => i.getKeyword)

    println(s"converted from ${emitsJ.getClass} to ${emits.getClass}")

    //return(emits)
    return (List.empty[String])
  }

  trieInit(List("hello"))
  patternTest("hello")
}

收率:

converted from class java.util.ArrayList to class scala.collection.immutable.$colon$colon

现在更改为仅通过更改return行 -

来返回实际值
import org.ahocorasick.trie._
import scala.collection.JavaConverters._    // convert Java colllections to Scala ones

object wierd {

  val trie = new Trie

  def trieInit(patterns: List[String]) {
    trie.onlyWholeWords();
    for (pattern <- patterns)
      trie.addKeyword(pattern)
  }

  def patternTest(text : String) : List[String] = 
  {
    val emitsJ = trie.parseText(text)
    val emits = emitsJ.asScala map (i => i.getKeyword)

    println(s"converted from ${emitsJ.getClass} to ${emits.getClass}")

    return(emits)
    //return (List.empty[String])
  }

  trieInit(List("hello"))
  patternTest("hello")
}

产生编译错误:

[error] reproduce.scala:23: type mismatch;
[error]  found   : Iterable[String]
[error]  required: List[String]
[error]     return(emits)
[error]            ^
[error] one error found
[error] (compile:compile) Compilation failed

对此有什么简单的解释? 我怎样才能更好地接近转换?

3 个答案:

答案 0 :(得分:1)

JavaConverters转换为最接近它所拥有的Java集合的Scala集合。您仍然需要调用toList以进一步将其转换为您想要的集合:

val emits = emitsJ.asScala.toList map (i => i.getKeyword)

参见相关内容:What is the difference between JavaConverters and JavaConversions in Scala?

答案 1 :(得分:1)

trie.parseText的返回类型声明为java.util.Collection[Emit]。这不是非常具体,Collection有很多可能的子类型,它们没有指定它们要返回的具体类型,它可能是TreeSet,它可能是Vector 1}},它可能是ArrayList。但就编译器而言,它可能是Collection的子类型。您已经在运行时检查过它,并发现对于某些特定输入,它碰巧返回ArrayList,但编译器无法知道这一点。

当您在此调用.asScala时,您正在使用JavaConverters

中的隐含定义
implicit def iterableAsScalaIterableConverter[A](i: java.lang.Iterable[A]): convert.Decorators.AsScala[Iterable[A]]

将java.lang.Itaerable转换为scala.collection.Iterable。

Collection没有转换器,因此您可以获得下一个最具体的转换器Iterable。您在此Iterable上调用地图并返回Iterable

现在,就像你已经检查了从parseText返回的Collection的运行时值,看到它是ArrayList你已经检查了这个map操作返回的值,并且看到它是一个scala.collection.immutable.List,但同样,编译器无法知道这一点,它可以知道的是你得到的东西是Iterable的子类。

只需在生成的Iterable上调用.toList就可以了。

答案 2 :(得分:1)

所以这里发生的事情是从asScala返回给你的基础对象是List,但是从Iterable开始被归为List <: Iterable,这一切都很好,直到您想使用Iterable作为列表。最简单的选择是在其上调用toList

有关详细信息,您可以浏览source

  case class JCollectionWrapper[A](underlying: ju.Collection[A]) extends AbstractIterable[A] with Iterable[A] {
    def iterator = underlying.iterator
    override def size = underlying.size
    override def isEmpty = underlying.isEmpty
    def newBuilder[B] = new mutable.ArrayBuffer[B]
  }

所以你对asScala的调用会让你回到这个包装器。接下来,您对地图的调用是使用this CanBuildFrom,然后在this map operation中使用,最后会为您提供恰好是List的结果,因为构建器是ListBufferresultList,它恰好是Iterable,因为它可以从模式向下转换为Iterable。希望能解释一切:)