2014-09-16 3 views
3

Преобразование из java.util.ArrayList в scala.collection.immutable.List, то 2,10 компилятор и времени выполнения может показаться, что в несогласии, о типе val emits:Различия типа Scala между компилятором и временем выполнения?

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 

Что бы простое объяснение для этого? Как мне лучше подойти к конверсии?

ответ

1

JavaConverters конвертирует в коллекцию Scala, ближайшую к коллекции Java, которая имеет pimped. Вы все еще должны вызвать ToList для дальнейшего преобразования его в коллекцию вы хотите:

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

Показать похожие: What is the difference between JavaConverters and JavaConversions in Scala?

1

возвращаемый тип trie.parseText объявлен как java.util.Collection[Emit]. Это не очень специфично, существует множество возможных подтипов Collection, они не указывают, какой конкретный тип они собираются вернуть, это может быть TreeSet, это может быть Vector, это может быть 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 назад.

Теперь, так же, как вы проверите значение времени выполнения Collection вернулся из parseText и увидел, что это был ArrayList вы осмотрели возвращаемое значение этой операции карты и увидели, что это scala.collection.immutable.List, но опять же, компилятор не может этого знать, все, что он может знать, это то, что вы получили что-то, что является подклассом Iterable.

Просто позвоните .toList на полученный Iterable должно быть все, что вам нужно сделать.

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 дает Вам эту обертку. Далее, ваш вызов карты использует thisCanBuildFrom, который затем используется в this map operation, что в конечном итоге дает результат, который случается быть List, потому что строитель является ListBuffer и result является List, что опять-таки случается быть Iterable потому что можно построить из шаблонов downcasts до Iterable. Надеюсь, что все это объясняет :)

+0

Спасибо @ Nooah! Можете ли вы помочь мне соединить точки в точках - где именно во всех случаях это происходит вначале? – matanster

+0

@matt Убивание происходит в начале, объект, который 'asScala' возвращает, опущен до' Iterable', так как вы не возвращаете тип 'JCollectionWrapper'. Из их шаблона «CanBuildFrom» сохраняется тип, переданный ему, поэтому он сохраняет «Iterable» вместо изменения в «List», даже если это то, что вы получаете. – Noah