首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用有状态PartialFunction和collectFirst时的NullPointerException

使用有状态PartialFunction和collectFirst时的NullPointerException
EN

Stack Overflow用户
提问于 2011-08-18 05:52:50
回答 1查看 223关注 0票数 0

考虑一下这个(非常难看的代码):

代码语言:javascript
复制
object ExternalReferences2 {
  import java.util.regex._

  implicit def symbol2string(sym: Symbol) = sym.name

  object Mapping {
    def fromXml(mapping: scala.xml.NodeSeq) = {
      new Mapping(mapping \ 'vendor text, 
                  mapping \ 'match text, 
                  mapping \ 'format text)
    }
  }
  case class Mapping(vendor: String, 
                     matches: String,
                     format: String) extends PartialFunction[String, String] {
    private val pattern = Pattern.compile(matches)
    private var _currentMatcher: Matcher = null
    private def currentMatcher = 
      { println("Getting matcher: " + _currentMatcher); _currentMatcher }
    private def currentMatcher_=(matcher: Matcher) = 
      { println("Setting matcher: " + matcher); _currentMatcher = matcher }

    def isDefinedAt(entity: String) = 
      { currentMatcher = pattern.matcher(entity); currentMatcher.matches }

    def apply(entity: String) = apply

    def apply = {
      val range = 0 until currentMatcher.groupCount()
      val groups = range 
                     map (currentMatcher.group(_)) 
                     filterNot (_ == null) 
                     map (_.replace('.', '/'))
      format.format(groups: _*)
    }
  }

  val config =
    <external-links>
      <mapping>
        <vendor>OpenJDK</vendor>
        <match>{ """^(javax?|sunw?|com.sun|org\.(ietf\.jgss|omg|w3c\.dom|xml\.sax))(\.[^.]+)+$""" }</match>
        <format>{ "http://download.oracle.com/javase/7/docs/api/%s.html" }</format>
      </mapping>
    </external-links>

  def getLinkNew(entity: String) =
     (config \ 'mapping) 
       collectFirst({ case m => Mapping.fromXml(m)})
       map(_.apply)

  def getLinkOld(entity: String) =
    (config \ 'mapping).view 
      map(m => Mapping.fromXml(m)) 
      find(_.isDefinedAt(entity)) 
      map(_.apply)
}

我试图通过使用collectFirst来改进getLinkOld方法,如getLinkNew中所示,但我总是得到一个NullPointerException,因为_currentMatcher仍然设置为null

代码语言:javascript
复制
scala> ExternalReferences2.getLinkNew("java.util.Date")
Getting matcher: null
java.lang.NullPointerException
    at ExternalReferences2$Mapping.apply(<console>:32)
    at ExternalReferences2$$anonfun$getLinkNew$2.apply(<console>:58)
    at ExternalReferences2$$anonfun$getLinkNew$2.apply(<console>:58)
    at scala.Option.map(Option.scala:131)
    at ExternalReferences2$.getLinkNew(<console>:58)
    at .<init>(<console>:13)
    at .<clinit>(<console>)
    at .<init>(<console>:11)
    at .<clinit>(<console>)

虽然它可以完美地与getLinkOld配合使用。

这里的问题是什么?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2011-08-18 23:17:21

isDefined中,您的匹配器是作为副作用创建的。将副作用函数传递给诸如map之类的例程通常会导致灾难,但这并不是这里发生的事情。您的代码要求在调用apply之前调用isDefined,并使用相同的参数。这使得你的代码非常脆弱,这就是你应该改变的。

一般情况下,PartialFunction的客户端不必遵循该协议。例如,想象一下

代码语言:javascript
复制
if (f.isDefinedAt(x) && f.isDefinedAt(y)) {fx = f(x); fy = f(y)}. 

这里调用apply的代码甚至不是你的,而是集合类的,所以你不能控制发生的事情。

你在getLinkNew中遇到的具体问题是,isDefined是called.The PartialFunctioncollectFirst的参数是{case m => ...}。调用的isDefined是此函数的isDefined。因为m是一个无可辩驳的模式,所以它总是正确的,并且collectFirst将总是返回第一个元素(如果有)。partial函数返回另一个碰巧没有在m中定义的部分函数( Mapping),这是不相关的。

编辑-可能的解决方法

一个非常小的更改是检查matcher是否可用,如果不可用,则创建它。最好也保留用于创建它的entity字符串,这样就可以检查它是否是正确的字符串。只要不存在多线程,这就应该使副作用变得良性。但是顺便说一句,不要使用null,使用Option,所以编译器不会让你忽略它可能是None的可能性。

代码语言:javascript
复制
var _currentMatcher : Option[(String, Matcher)] = None
def currentMatcher(entity: String) : Matcher = _currentMatcher match{
  case Some(e,m) if e == entity => m
  case _ => {
    _currentMatcher = (entity, pattern.matcher(entity))
    _currentmatcher._2
  }
}

再次编辑。愚蠢的我

抱歉,所谓的变通方法确实使类更安全,但它并不能使collectFirst解决方案工作。同样,case m =>分部函数总是被定义的(注意:entity甚至没有出现在您的getLinkNew代码中,这应该是令人担忧的)。问题是需要一个实体的PartialFunction (而不是NodeSeq的实体,它将为函数所知,但不会作为参数传递)。isDefined将被调用,然后应用。模式和匹配器依赖于NodeSeq,所以它们不能预先创建,只能在isDefined和/或apply中创建。同样,您可以缓存在isDefined中计算的内容,以便在Apply中重用。这绝对不是很好

代码语言:javascript
复制
def linkFor(entity: String) = new PartialFunction[NodeSeq, String] {
  var _matcher : Option[String, Matcher] = None
  def matcher(regexp: String) = _matcher match {
    case Some(r, m) where r == regexp => m
    case None => {
      val pattern = Pattern.compile(regexp)
      _matcher = (regexp, pattern.matcher(entity))
      _matcher._2
    }
  }
  def isDefined(mapping: NodeSeq) = {
    matcher(mapping \ "match" text).matches
  }
  def apply(mapping: NodeSeq) = {
     // call matcher(...), it is likely to reuse previous matcher, build result
  }

}

您可以将其与(config \ mapping).collectFirst(linkFor(entity))一起使用

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/7100003

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档