4

私はScalaで効果的に列挙型である何かを実装しようとしています。コンパイラが非網羅的なパターンの一致を検出できるように、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
...
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

明らかな解決策は、ケースオブジェクトを別々の名前空間に配置することです。

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
}

この場合、「左」は型ではなくインスタンスであるため、これは理にかなっています。型を参照する簡単な方法があると思います。それを達成するために私が得ることができる最も近いものはこれです:

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

}

しかし、これによりマッチブロックのコンパイルは正常になりますが、実際にこれらのオブジェクトを参照する方法を見つけることができませんでした。たとえば、この「列挙」のメンバーを関数に渡すことができませんでした。これは、Horizo​​ntalAlignmentが型であり、オブジェクトではないため、フィールドアクセスを使用してネストされたオブジェクトの1つを参照することができないためです。一方、これらのオブジェクトは型ではないため、 「#」記号。

クラスの外部からクラスにネストされたオブジェクトを参照する方法はありますか?

編集

これまでのところ、パッケージオブジェクトがこの問題を解決するための最良の方法であることがわかりました。

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)

}

ただし、上記の質問(クラス内のネストされたオブジェクトへのアクセス)は引き続き有効です。

4

2 に答える 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

発生した問題は、Horizo​​ntalAlignmentが抽象クラスであるため、逆参照するHorizo​​ntalAlignmentのインスタンスがないことでした。元の名前空間の定式化では、Horizo​​ntalAlignmentインスタンスをインスタンス化する必要があり、内部オブジェクトはそのインスタンスに固有になります。ただし、Horizo​​ntalAlignmentは封印されているため、定義されたもの以外のコンパイルユニットでそのようなインスタンスを作成することはできません。そのため、列挙値を実際に取得することはできません。

Javaとは異なり、クラスに関連付けられた「静的名前空間」はありません。同等のものを取得するには、コンパニオンオブジェクトを使用する必要があります。

于 2011-04-23T17:08:33.107 に答える
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
于 2011-04-24T06:31:59.080 に答える