首页 文章

Scala中嵌套类型的模式匹配

提问于
浏览
4

我试图在Scala中实现一些有效的枚举 . 我想使用案例类来做到这一点,以便编译器能够检测任何非详尽的模式匹配 .

这在基本形式下工作正常,例如:

sealed abstract class HorizontalAlignment
case object Left extends HorizontalAlignment
case object Right extends HorizontalAlignment
case object Center extends HorizontalAlignment
case object AsIs extends HorizontalAlignment
...
def test (x : HorizontalAlignment) = 
  x match {
    case Left => ...
    ...  
  }

但是这并不理想,因为案例对象的名称很容易发生冲突:

sealed abstract class HorizontalAlignment
case object Left extends HorizontalAlignment
case object Right extends HorizontalAlignment
case object Center extends HorizontalAlignment
case object AsIs extends HorizontalAlignment

sealed abstract class VerticalAlignment
case object Top extends VerticalAlignment
case object Bottom extends VerticalAlignment
case object Center extends VerticalAlignment
case object AsIs extends VerticalAlignment

// "Center" and "AsIs" clash

显而易见的解决方案是将case对象放入单独的命名空间:

sealed abstract class HorizontalAlignment {
  case object Left extends HorizontalAlignment
  case object Right extends HorizontalAlignment
  case object Center extends HorizontalAlignment
  case object AsIs extends HorizontalAlignment
}

sealed abstract class VerticalAlignment {
  case object Top extends VerticalAlignment
  case object Bottom extends VerticalAlignment
  case object Center extends VerticalAlignment
  case object AsIs extends VerticalAlignment
}

但是如何在匹配块中引用这些类?

它们不能用Java样式的点引用:

def test (x : HorizontalAlignment) = 
x match {
  case HorizontalAlignment.Left => 0  //  error: not found: value HorizontalAlignment
}

“#”符号似乎也不起作用:

def test (x : HorizontalAlignment) = 
x match {
  case HorizontalAlignment#Left => 0 // error: '=>' expected but '#' found 
}

这种形式也不起作用:

def test (x : HorizontalAlignment) = 
x match {
  case _ : HorizontalAlignment#Left => 0  // error: type Left is not a member of Test.HorizontalAlignment
}

这是有道理的,因为“Left”在这种情况下是一个实例而不是一个类型,我怀疑有一种简单的方法来引用该类型 . 我能达到的最接近的是:

sealed abstract class HorizontalAlignment {
  case class Left extends HorizontalAlignment
  case class Right extends HorizontalAlignment
  case class Center extends HorizontalAlignment
  case class AsIs extends HorizontalAlignment

  object Left
  object Right
  object Center
  object AsIs

}

但是虽然这使得匹配块编译正常,但我找不到任何方法来实际引用这些对象,例如将此“枚举”的成员传递给函数 . 这是因为HorizontalAlignment是一个类型而不是一个对象,因此不可能使用字段访问来引用其中一个嵌套对象,另一方面,这些对象不是类型,所以不可能使用它来引用它们 . “#”符号 .

Is there any way to refer to objects nested in a class from outside that class?

EDIT

到目前为止,我发现包对象是解决此问题的最佳方法 .

package object HorizontalAlignment  {
  sealed abstract class HorizontalAlignment
  case object Left extends HorizontalAlignment
  case object Right extends HorizontalAlignment
  case object Center extends HorizontalAlignment
  case object AsIs extends HorizontalAlignment
}

package object VerticalAlignment {
  sealed abstract class VerticalAlignment 
  case object Top extends VerticalAlignment 
  case object Bottom extends VerticalAlignment 
  case object Center extends VerticalAlignment 
  case object AsIs extends VerticalAlignment 
}


object Test {
  import HorizontalAlignment.HorizontalAlignment
  import VerticalAlignment.VerticalAlignment 

  def test (x : HorizontalAlignment, y : VerticalAlignment) =  {
    x match {
      case HorizontalAlignment.Left => ...
      ...
    }

    y match {
      case VerticalAlignment.Top => ...
      ...
    }
  }

  def testTest = test (HorizongalAlignment.Left, VerticalAlignment.Top)

}

但是,上述问题(访问类中的嵌套对象)仍然存在 .

2 回答

  • 11

    您不必使用包对象,这可能具有一些其他不合需要的语义:常规的旧伴随对象也同样好:

    sealed trait HorizontalAlignment
    object HorizontalAlignment {
      case object Left extends HorizontalAlignment
      case object Right extends HorizontalAlignment
      case object Center extends HorizontalAlignment
      case object AsIs extends HorizontalAlignment
    }
    
    scala> def test (x : HorizontalAlignment) = x match {
         |   case HorizontalAlignment.Left => "got left"
         | }
    
    scala> test(HorizontalAlignment.Left)
    res0: java.lang.String = got left
    

    您遇到的问题是,由于HorizontalAlignment是一个抽象类,因此没有要取消引用的HorizontalAlignment实例 . 使用原始的命名空间公式,您需要实例化HorizontalAlignment实例,并且内部对象将特定于该实例 . 但是,由于HorizontalAlignment是密封的,因此无法在任何其他编译单元中创建这样的实例,而不是定义它的实例,因此实际上永远无法通过任何方式获取枚举值 .

    与Java不同,没有与类关联的“静态命名空间”;为了获得等价物,你必须使用一个伴侣对象 .

  • 2

    你已经明智地避开了这个结构,但要回答剩下的问题:要引用一个没有实例的类的成员,你将不得不求助于存在 .

    sealed abstract class HorizontalAlignment {
      case object Left extends HorizontalAlignment
      case object Right extends HorizontalAlignment
      case object Center extends HorizontalAlignment
      case object AsIs extends HorizontalAlignment
    }
    
    object Test {
      type LeftOb = x.Left.type forSome { val x: HorizontalAlignment }
    
      def test(x: HorizontalAlignment): Int = x match {
        case _: LeftOb => 0
      }
    }
    

    不出所料(好吧,不出所料,如果你是我)试图在模式匹配中使用该类型会使编译器中的bejeezus崩溃 . 但原则上它是表达它的方式 .

    编辑:当我指出模式匹配器崩溃时,人们似乎分心了 . 让我以一种不那么崩溃的方式说明:a)这是表达有问题概念的唯一方法,b)它是有效的 .

    sealed abstract class HorizontalAlignment {
      case object Left extends HorizontalAlignment
      case object Right extends HorizontalAlignment
      case object Center extends HorizontalAlignment
      case object AsIs extends HorizontalAlignment
    }
    
    object Test {
      type LeftOb = x.Left.type forSome { val x: HorizontalAlignment }
    
      def f(x: Any) = x.isInstanceOf[LeftOb]
    
      def main(args: Array[String]): Unit = {
        val ha = new HorizontalAlignment { }
        println(f(ha.Left))
        println(f(ha.Right)) 
      }
    }
    

    输出:

    true
    false
    

相关问题