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 _ =>
}
}
精彩评论