开发者

Java generics <?> and how to deal with it in Scala?

I have an API (from third party java library) that looks like:

    public List<?> getByXPath(String xpathExpr)

defined on a class called DomNode

I try this in scala function:

1: def removeChild(node: DomNode, xpath: String) {
2:               val lst: List[?] =  node.getByXPath(xpath)
3:      val child: DomNode = lst(0)
4:                child.getParentNode().removeChild(child)
    }

but it does not compile in scala. I get error in line 2.

As per the answers I modified it and no开发者_StackOverflow中文版w it is:

1: def removeChild(node: DomNode, xpath: String) {
2:            val lst = node.getByXPath(xpath)
3:            val child = lst(0).asInstanceOf[DomNode]            
4:            child.getParentNode().removeChild(child)
    }

Now I get error on line 3: lst of type java.util.List[?0] does not take parameters

I also tried val lst: List[_] = node.getByXPath(xpath) but this gives me error right on this same line:

type mismatch;
 found : java.util.List[?0] where type ?0
 required: scala.List[_]

So I am still stuck.


Re your error on line 3: remember that it is a Java list, not a Scala list, so try

val child = lst.get(0).asInstanceOf[DomNode]


Here's what's wrong with your code:

def removeChild(node: DomNode, xpath: String) {
     val lst: List[?] =  node.getByXPath(xpath)
           /* ^^^^^^^ This probably refers to scala.collection.immutable.List,
                      which is a totally different type from the 
                      java.util.List that getByXPath returns.*/
          /* Also, the ? needs to be changed into an _ in Scala */


     val child: DomNode = lst(0)
           /* Two problems here: First, java.util.List won't support indexing 
              with parentheses. Second, you need to typecast the result to get a
              DomNode. */


     child.getParentNode().removeChild(child)
}

Here's a corrected version:

import scala.collection.JavaConversions._
def removeChild(node:DomNode, xpath:String) {
   val lst:scala.collection.Seq[_] = node.getByXPath(xpath)
       /* triggers an implicit conversion that wraps the Java List in a Scala Seq */

   val child: DomNode = lst(0).asInstanceOf[DomNode]
   child.getParentNode().removeChild(child)
}

(Leaving out the type annotation will still work -- instead of causing the conversion when you assign val lst, it will cause the conversion when you try to call lst(0).)

Another corrected version that doesn't convert to a Scala Seq:

def removeChild(node:DomNode, xpath:String) {
   val lst:java.util.List[_] = node.getByXPath(xpath)
       /* you can remove the type annotation here, but I left it in
          for pedagogical purposes */

   val child: DomNode = lst.get(0).asInstanceOf[DomNode]
   child.getParentNode().removeChild(child)
}


The following worked for me:

val arr = DomNode.getByXPath("foo").toArray
val child = lst(0).asInstanceOf[DomNode]

Apparently there is no implicit conversion for wildcarded Java lists to Scala lists, therefor, the val lst you were creating was of the type java.util.list which of course does not have an apply method. I worked around this by just creating a Scala array from the list.


This should work:

val lst: java.util.List[_] =  node.getByXPath(xpath)


Here's my take on it:

def removeChild(node: DomNode, xpath: String) {
    // Let type inference do the work for you
    // val lst: List[?] =  node.getByXPath(xpath)
    val lst = node.getByXPath(xpath)

    // Also, do not confuse Java's and Scala's collections
    // val child: DomNode = lst(0)
    val child = lst.get(0)

    // Finally, since you do not have a static guarantee of the type,
    // match on it
    //     child.getParentNode().removeChild(child)
    child match {
        case domNode: DomNode => domNode.getParentNode().removeChild(domNode)
        case _ =>
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜