Scala type inconsistency between compiler and runtime?

Conversion from java.util.ArrayList

to scala.collection.immutable.List

, compiler 2.10 and runtime may seem inconsistent in type 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")
}

      

Productivity:

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

Now, to return the real value by changing only the line 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")
}

      

Sets compile error:

[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

      

What would be the simple explanation for this? How do I best approach conversion?

+3


source to share


3 answers


JavaConverters converts to a Scala collection closest to a Java collection that has a pimped. You also need to call toList to convert it to the desired collection:

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

      



See also: What is the difference between JavaConverters and JavaConversions in Scala?

+1


source


the return type is trie.parseText

declared as java.util.Collection[Emit]

. This is not very specific, there are many possible subtypes Collection

, they do not indicate what specific type they will return, it might be TreeSet

, it might be Vector

, it might be ArrayList

. But as far as the compiler is concerned, it could be anything that is a subtype Collection

. You checked it at runtime and saw that there was a return for some specific input ArrayList

, but the compiler doesn't know about it.

When you call .asScala you are using this implicit definition from JavaConverters

implicit def iterableAsScalaIterableConverter[A](i: java.lang.Iterable[A]): convert.Decorators.AsScala[Iterable[A]]

      

Which converts java.lang.Itaerable to scala.collection.Iterable.



There is no converter for Collection

, so you get a converter for the next most specific thing Iterable

. you call the card on this Iterable

and return Iterable

.

Now, just like you checked the runtime value Collection

returned from parseText and saw what it is ArrayList

, you checked the value returned from this map operation and saw what it is scala.collection.immutable.List

, but again, the compiler cannot know that, all what it might know is that you got something that is subclassed Iterable

back.

Just calling .toList on the result Iterable

should be all you need to do.

+1


source


So what happens is that the underlying object returned to you from asScala

is this List

, but it has been disabled before Iterable

, since that's List <: Iterable

all fine until you want to use it Iterable

as a list. The easiest option is to call toList

on it.

For more details, you can view :

  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]
  }

      

So your call asScala

returns this wrapper. Then your call to the card using the this CanBuildFrom

, which is then used in the operation with the card , which finally gives a result that is List

because the builder ListBuffer

and result

is List

that, again, is Iterable

because it can build from templates downcasts up Iterable

. Hope that explains everything :)

+1


source







All Articles