【问题标题】:How do I create an enum in scala that has an extra field如何在具有额外字段的 scala 中创建枚举
【发布时间】:2011-09-22 08:09:37
【问题描述】:

在 Java 中我有这样的东西

public enum FlatFileHeaderMapping {

   HEADER_EL(1),
   HEADER_RESERVED1(5),
   HEADER_RESERVED2(2),
   HEADER_MESSAGE_TYPE(4)

   public final int fieldSize;

    private FlatFileHeaderMapping(int fieldSize) {
        this.fieldSize = fieldSize;
   }

}

然后我可以使用它将每一行放入地图中,然后通过此枚举访问地图中的键(如符号)

就我所见,枚举不具备这种品质,并且案例类不像枚举声明那样排序 - 因此不能用于匹配如上所示的记录布局。至少不是没有有序集合的支持。

我可能遗漏了一些明显的东西,因此问题!

谢谢

【问题讨论】:

  • 这里还有其他问题有很好的答案:stackoverflow.com/questions/1898932/… 和这里:stackoverflow.com/questions/1321745/…
  • 看到到目前为止的答案,我还应该注意到 Java 枚举相对于 case 对象的另一个优势在于它们保留了声明的顺序。如果曾经想要匹配记录的布局,这是一个有趣的属性。必须将 IIANM 案例对象放入链接列表中才能共享该属性。
  • Viktor 给出了迄今为止最好的答案。虽然比 Java 版本更冗长,但更灵活。

标签: scala enums


【解决方案1】:
object Direction extends Enumeration {
  val North = Value("North")
  val East = Value("East")
  val South = Value("South")
  val West = Value("West")
}

scala> import Direction._
scala> values foreach println
scala> val map = HashMap(North -> 1, South -> 2)

【讨论】:

  • 问题在于我无法定义数值。 Scala 中的枚举默认有一个索引和一个值。不可能添加重复索引(有充分的理由)......因此我需要一种向枚举添加额外属性的好方法
【解决方案2】:

你可以试试case objects:

sealed trait FlatFileHeaderMapping { val fieldSize: Int }                                                                                                                                                                          
case object HEADER_EL extends FlatFileHeaderMapping { val fieldSize = 1 }                                                                                                  
case object HEADER_RESERVED1 extends FlatFileHeaderMapping { val fieldSize = 5 }                                                                                           
case object HEADER_RESERVED2 extends FlatFileHeaderMapping { val fieldSize = 2 }                                                                                           
case object HEADER_MESSAGE_TYPE extends FlatFileHeaderMapping { val fieldSize = 4 } 

然后你可以像这样使用枚举:

object Test {                                                                                                                                                              
  def foo(x: FlatFileHeaderMapping) {                                                                                                                                      
    val result =                                                                                                                                                           
      x match {
        case HEADER_EL => "it's a HEADER_EL!"                                                                                                                              
        case other => "its field size is: " + other.fieldSize                                                                                                             
      }                                                                                                                                                                    
    println(result)                                                                                                                                                        
  }                                                                                                                                                                        

  def main(args: Array[String]) {                                                                                                                                          
    foo(HEADER_EL)                                                                                                                                                         
    foo(HEADER_MESSAGE_TYPE)                                                                                                                                               
  }                                                                                                                                                                        
}

您在这里获得的主要好处是编译时检查所有枚举值是否已处理。即在上面的x match { ... } 代码中,如果你没有'case other => ...` 子句,你会得到一个编译错误。

我只是重申this answer,它列出了这种方法的优缺点。

【讨论】:

  • 我接受这是一个正确的答案。然而,如果这是我们在 Scala 中能做的最好的事情,我仍然不满意(当然不是责怪你!)。在这种情况下(这是我所知道的为数不多的情况之一)Java 表现得更加简洁明了。这些属性通常对 Scala 有利,因此我缺乏热情。感谢您的回复。
  • 更正 - Viktor 改进了问题
【解决方案3】:

过度思考是对的,但声明案例对象的方式不那么冗长:

sealed abstract class FlatFileHeaderMapping(val fieldSize: Int)
case object HEADER_EL extends FlatFileHeaderMapping(1)
case object HEADER_RESERVED1 extends FlatFileHeaderMapping(5)
case object HEADER_RESERVED2 extends FlatFileHeaderMapping(2)
case object HEADER_MESSAGE_TYPE extends FlatFileHeaderMapping(4)

【讨论】:

  • 我喜欢这个答案,因为符号不那么冗长。然而,我仍然有点不安,因为与 Java 实现相比,这感觉很老套——Java 不那么冗长,而且感觉更好。
  • 我已编辑问题以添加这样一个事实,即此类对象不像枚举声明那样排序。因为我想使用 Scala 来处理遗留文件和记录布局,所以我想要一个相等或更好的符号。感谢您到目前为止的回答,如果您有任何进一步的想法,我会很感兴趣。干杯
  • 是的,我总体上喜欢 Scala,但对于这样的一些事情,它变得有点难看。我自己还在学习 Scala,我真的不知道有什么更好的方法。
  • 这里的关键是密封的 abstract class 而不是 trait - 它允许为枚举提供参数
【解决方案4】:

Enumeration with constructor and lookup table回答了这个问题

整数值存在一个更简单的解决方案:

object FlatFileHeaderMapping extends Enumeration  {
   type FlatFileHeaderMapping = Value 
   val HEADER_EL = Value(1, "HEADER_EL")
   val HEADER_RESERVED1 = Value(5, "HEADER_RESERVED1")
   val HEADER_RESERVED2 = Value(2, "HEADER_RESERVED2")
   val HEADER_MESSAGE_TYPE = Value(4, "HEADER_MESSAGE_TYPE")
}

【讨论】:

    【解决方案5】:

    复制已接受答案的内容,因为它隐藏在损坏的 Tumblr 链接(我通过 Archive.org 访问)后面,而该链接又指向 this page

    trait Enum { //DIY enum type
      import java.util.concurrent.atomic.AtomicReference //Concurrency paranoia
    
      type EnumVal <: Value //This is a type that needs to be found in the implementing class
    
      private val _values = new AtomicReference(Vector[EnumVal]()) //Stores our enum values
    
      //Adds an EnumVal to our storage, uses CCAS to make sure it's thread safe, returns the ordinal
      private final def addEnumVal(newVal: EnumVal): Int = { import _values.{get, compareAndSet => CAS}
        val oldVec = get
        val newVec = oldVec :+ newVal
        if((get eq oldVec) && CAS(oldVec, newVec)) newVec.indexWhere(_ eq newVal) else addEnumVal(newVal)
      }
    
      def values: Vector[EnumVal] = _values.get //Here you can get all the enums that exist for this type
    
      //This is the trait that we need to extend our EnumVal type with, it does the book-keeping for us
      protected trait Value { self: EnumVal => //Enforce that no one mixes in Value in a non-EnumVal type
        final val ordinal = addEnumVal(this) //Adds the EnumVal and returns the ordinal
    
        def name: String //All enum values should have a name
    
        override def toString = name //And that name is used for the toString operation
        override def equals(other: Any) = this eq other.asInstanceOf[AnyRef]
        override def hashCode = 31 * (this.getClass.## + name.## + ordinal)
      }
    }
    
    //And here's how to use it, if you want compiler exhaustiveness checking
    object Foos extends Enum {
      sealed trait EnumVal extends Value /*{ you can define your own methods etc here }*/
    
      val F = new EnumVal { val name = "F" }
      val X = new EnumVal { val name = "X" }
    }
    
    /**
    scala> Foos.values.find(_.name == "F")
    res3: Option[Foos.EnumVal] = Some(F)
    
    scala> Foos.X.ordinal
    res4: Int = 1
    
    scala> def doSmth(foo: Foos.EnumVal) = foo match {
      case Foos.X => println("pigdog")
    }
    
    <console>:10: warning: match is not exhaustive!
    missing combination        $anon$1
    missing combination        $anon$2
    
    scala> def doSmth(foo: Foos.EnumVal) = foo match {
             case Foos.X => println("pigdog")
             case Foos.F => println("dogpig")
           }
    doSmth: (foo: Foos.EnumVal)Unit
    **/
    
    //But if you don't care about getting exhaustiveness warnings, you can do:
    
    object Foos extends Enum {
      case class EnumVal private[Foos](name: String) extends Value /* { you can define your own methods and stuff here } */
    
      val F = EnumVal("F")
      val X = EnumVal("X")
    }
    
    /**
    Which is a bit less boilerplatey.
    
    
    Cheers,
    √
    **/
    

    【讨论】:

      猜你喜欢
      • 2013-11-21
      • 1970-01-01
      • 1970-01-01
      • 2018-10-04
      • 2021-12-22
      • 1970-01-01
      • 2016-08-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多