模式匹配 - 值不是绑定变量的成员

问题描述

我正在研究 Scala(版本 2.13.2),在这里我定义了一个带有特征 ListSeq 的简单链表。另外,我想覆盖一个 toString 方法来进行漂亮的打印。为此,我决定使用模式匹配。在 assert 种情况下可以看到所需的结果

sealed trait ListSeq {
    override def toString: String = s"[$elemSequence]"
    private def elemSequence: String = {
        this match {
            case ListPair(hd,tl @ ListPair(_,_)) => s"$hd,${tl.elemSequence}"
            case ListPair(hd,EmptyList) => s"$hd"
            case EmptyList => ""
        }
    }
}
case class ListPair(head: Int,tail: ListSeq) extends ListSeq
case object EmptyList extends ListSeq

object ListSeqExample extends App {
    val seq1 = ListPair(1,ListPair(2,ListPair(3,EmptyList)))
    val seq2 = EmptyList
    
    assert(seq1.toString == "[1,2,3]")
    assert(seq2.toString == "[]")
}

问题

这段代码没有编译通过,错误是:

value elemSequence is not a member of ListPair
            case ListPair(hd,${tl.elemSequence}"

我不清楚为什么会出现这个错误。据我所知,Scala 可以匹配嵌套字段并将字段绑定到变量 - 就像在 case ListPair(hd,_)) 中所做的那样。但是从报错信息看,好像是猜不到绑定对象的类型(ListPair

奇怪的行为

一个有趣的事情是 - 如果我按照以下方式重新定义 ListSeq - 通过删除 elemSequence 方法,并且所有字符串创建都在 toString 中完成 - 有没有错误

sealed trait ListSeq {
    override def toString: String = this match {
        case ListPair(hd,${tl.toString}"
        case ListPair(hd,EmptyList) => s"$hd"
        case EmptyList => ""
    }
}

我知道 toString 的结果会略有不同(没有大括号),但这不是这里的重点。

问题

为什么在一种情况下会出错,而在另一种情况下却能顺利编译?

解决方法

它返回一个错误,因为 elemSequenceprivate 中定义的 ListSeq 方法,所以它在 ListPair 中不可见。

一种解决方案可能是将可见性更改为 protected(并添加修饰符 final,以便子类无法 override 方法):

sealed trait ListSeq {
    override def toString: String = s"[$elemSequence]"
    final protected def elemSequence: String = {
        this match {
            case ListPair(hd,tl @ ListPair(_,_)) => s"$hd,${tl.elemSequence}"
            case ListPair(hd,EmptyList) => s"$hd"
            case EmptyList => ""
        }
    }
}
case class ListPair(head: Int,tail: ListSeq) extends ListSeq
case object EmptyList extends ListSeq

object ListSeqExample extends App {
    val seq1 = ListPair(1,ListPair(2,ListPair(3,EmptyList)))
    val seq2 = EmptyList
    
    assert(seq1.toString == "[1,2,3]")
    assert(seq2.toString == "[]")
}

希望能帮到你。

,

这是一个私有方法。这些不能被子类继承或访问(在 Scala 或 Java 中)。

将其更改为 protected 或 public 或 package private 将解决此问题。