Как добавить метод в перечисление в Scala?

В Java вы могли бы:

public enum Enum {
    ONE {
        public String method() {
            return "1";
        }
    },
    TWO {
        public String method() {
            return "2";
        }
    },
    THREE {
        public String method() {
            return "3";
        }
    };

    public abstract String method();
}

Как вы делаете это в Scala?

РЕДАКТИРОВАТЬ / Полезные ссылки:

вопрос задан 3.12.2010
Etam
2061 репутация

11 ответов


  • 32 рейтинг

    Вот пример добавления атрибутов в перечисления scala путем расширения перечисления. Вал класс.

    object Planet extends Enumeration { 
       protected case class Val(val mass: Double, val radius: Double) extends super.Val { 
         def surfaceGravity: Double = Planet.G * mass / (radius * radius) 
         def surfaceWeight(otherMass: Double): Double = otherMass * surfaceGravity 
       } 
       implicit def valueToPlanetVal(x: Value) = x.asInstanceOf[Val] 
    
       val G: Double = 6.67300E-11 
       val Mercury = Val(3.303e+23, 2.4397e6) 
       val Venus   = Val(4.869e+24, 6.0518e6) 
       val Earth   = Val(5.976e+24, 6.37814e6) 
       val Mars    = Val(6.421e+23, 3.3972e6) 
       val Jupiter = Val(1.9e+27, 7.1492e7) 
       val Saturn  = Val(5.688e+26, 6.0268e7) 
       val Uranus  = Val(8.686e+25, 2.5559e7) 
       val Neptune = Val(1.024e+26, 2.4746e7) 
    } 
    
    scala> Planet.values.filter(_.radius > 7.0e6) 
    res16: Planet.ValueSet = Planet.ValueSet(Jupiter, Saturn, Uranus, Neptune) 
    
    ответ дан Sean Ross, с репутацией 321, 1.07.2011
  • 29 рейтинг

    Опираясь на решение Криса , вы можете добиться более приятного синтаксиса с неявным преобразованием:

    object Suit extends Enumeration {
       val Clubs, Diamonds, Hearts, Spades = Value
    
       class SuitValue(suit: Value) {
          def isRed = !isBlack
          def isBlack = suit match {
             case Clubs | Spades => true
             case _              => false
          }
       }
    
       implicit def value2SuitValue(suit: Value) = new SuitValue(suit)
    } 
    

    Тогда можно позвонить, например, Suit.Clubs.isRed.

    ответ дан Aaron Novstrup, с репутацией 16456, 3.12.2010
  • 12 рейтинг

    перечисления Scala отличаются от перечислений Java.

    В настоящее время нет способа добавить к нему методы (в здравом смысле). Есть некоторые обходные пути, но ничего, что работает во всех случаях и , не выглядит как синтаксический мусор.

    Я пробовал что-то похожее (добавление методов к перечисляемому экземпляру класса, в то же время имея возможность создавать новые экземпляры во время выполнения и имея рабочие отношения эквивалентности между экземплярами класса object и new), но было остановлено из-за ошибки # 4023 («getClasses / getDeclaredClasses, кажется, пропускает некоторые (REPL) или все (scalac) объявленные классы (объекты)»).

    Посмотрите на эти связанные вопросы мной:

    Честно говоря, я бы не использовал Enumeration. Это класс из Scala 1. 0 (2004), и в нем есть странные вещи, и не многие люди (кроме тех, кто написал это) понимают, как использовать его без учебника.

    Если бы мне абсолютно требовалось перечисление, я бы просто написал этот класс на Java.

    ответ дан soc, с репутацией 14716, 3.12.2010
  • 11 рейтинг

    Если вам не нужно перебирать значения перечисления или делать какие-либо другие вещи перечисления, я бы посоветовал использовать ADT вместо Enumeration.

    sealed abstract class Enum {
      def method: String = this match {
        case One => "1"
        case Two => "2"
        case Three => "3"
      }
    }
    case object One extends Enum
    case object Two extends Enum
    case object Three extends Enum
    

    Этот подход имеет одно преимущество перед Enumeration: компилятор предупредит вас, если вы забудете один или несколько случаев в выражении match.

    ответ дан missingfaktor, с репутацией 64851, 3.12.2010
  • 8 рейтинг

    Разработка решения Аарона , еще более компактной формы в Scala 2. 10, используя неявные классы :

    object Suit extends Enumeration {
       val Clubs, Diamonds, Hearts, Spades = Value
    
       implicit class SuitValue(suit: Value) {
          def isRed = !isBlack
          def isBlack = suit match {
             case Clubs | Spades => true
             case _              => false
          }
       }
    } 
    

    , а затем вы можете использовать его так: Suit.Clubs.isRed

    ответ дан ozeebee, с репутацией 1008, 29.01.2015
  • 7 рейтинг

    Вы можете сделать это:

    object Suit extends Enumeration {
      val Clubs, Diamonds, Hearts, Spades = Value
    
      def isRed(suit : Value) = !isBlack(suit)
      def isBlack(suit : Value) = suit match {
        case Clubs | Spades => true
        case _              => false
      }
    }
    

    Очевидно, что это не идеально, но вы можете сделать:

    Suit.isBlack(Suit.Clubs)
    
    ответ дан oxbow_lakes, с репутацией 105327, 3.12.2010
  • 4 рейтинг

    Перечисление Scala не позволяет добавлять свойства и / или методы к значениям в вашем перечислении. С этим новым MyEnumeration вы можете.

    abstract class MyEnumeration {
      // "Value" must be the name of the class defining your values type Value
      type Value
    
      // Contains your values in definition order
      private val vals = collection.mutable.LinkedHashMap[String, Value]()
    
      // A mixin for your values class to automatically collect the values
      protected trait ValuesCollector { self: Value =>
        private val ordinal = vals.size
    
        vals += (fieldNames(ordinal) -> self)
    
        def getName = fieldNames(ordinal)
        override def toString = getName
      }
    
      def apply(ordinal: Int) = vals(fieldNames(ordinal))
      def apply(fldName: String) = vals(fldName)
    
      def values = vals.values
      def namedValues: collection.Map[String, Value] = vals
    
      // Getting the field names through reflection.
      // Copied from scala.Enumeration
      private val fieldNames = getClass.getMethods filter (m =>
        m.getParameterTypes.isEmpty &&
        classOf[ValuesCollector].isAssignableFrom(m.getReturnType) &&
        m.getDeclaringClass != classOf[MyEnumeration]) map (_.getName)
    
    }
    

    Здесь вы видите пример Планеты в Scala.

    object Planet extends MyEnumeration {
    
      case class Value(val mass: Double, val radius: Double) extends ValuesCollector {
        // universal gravitational constant  (m3 kg-1 s-2)
        private val G = 6.67300E-11;
    
        def surfaceGravity = G * mass / (radius * radius)
        def surfaceWeight(otherMass: Double) = otherMass * surfaceGravity
    
      }
    
      val MERCURY = Value(3.303e+23, 2.4397e6)
      val VENUS = Value(4.869e+24, 6.0518e6)
      val EARTH = Value(5.976e+24, 6.37814e6)
      val MARS = Value(6.421e+23, 3.3972e6)
      val JUPITER = Value(1.9e+27, 7.1492e7)
      val SATURN = Value(5.688e+26, 6.0268e7)
      val URANUS = Value(8.686e+25, 2.5559e7)
      val NEPTUNE = Value(1.024e+26, 2.4746e7)
      val PLUTO = Value(1.27e+22, 1.137e6)
    
    }
    
    object PlanetTest {
      def main(args: Array[String]) {
        val earthWeight = 175
        val mass = earthWeight/Planet.EARTH.surfaceGravity
        for (p <- Planet.values) println("Your weight on %s is %f" format (p, p.surfaceWeight(mass)))
        /* Your weight on MERCURY is 66.107583
         * Your weight on VENUS is 158.374842
         * Your weight on EARTH is 175.000000
         * Your weight on MARS is 66.279007
         * Your weight on JUPITER is 442.847567
         * Your weight on SATURN is 186.552719
         * Your weight on URANUS is 158.397260
         * Your weight on NEPTUNE is 199.207413
         * Your weight on PLUTO is 11.703031
         */
      }
    
    } 
    
    ответ дан Jan van der Vorst, с репутацией 446, 29.06.2011
  • 2 рейтинг
    object Unit extends Enumeration {
      abstract class UnitValue(var name: String) extends Val(name) {
        def m: Unit
      }
      val G = new UnitValue("g") {
        def m {
            println("M from G")
        }
      }
      val KG = new UnitValue("kg") {
        def m {
            println("M from KG")
        }
      }
    }
    
    ответ дан Etam, с репутацией 2061, 29.09.2013
  • 1 рейтинг

    Если вам абсолютно необходимо иметь методы для каждого значения перечисления и , чтобы иметь возможность перебирать значения, вы можете сделать что-то вроде этого:

    object BatchCategory extends Enumeration {
      class BatchCategory extends Val {
        val isOfficial, isTest, isUser = false
      }
    
      val OFFICIAL = new BatchCategory { override val isOfficial = true }
      val TEST =     new BatchCategory { override val isTest = true }
      val USER =     new BatchCategory { override val isUser = true }
    
      // Needed to get BatchCategory from Enumeration.values
      implicit def valueToBatchCategory(v: Value): BatchCategory = v match {
        case bc: BatchCategory => bc
        case x => throw new IllegalArgumentException("Value is not a BatchCategory: " + x)
      }
    
      def valueOf(catStr: String): BatchCategory = {
        BatchCategory.values.
          find { v => val s = v.toString; s.take(1) == catStr || s == catStr }.
          getOrElse(throw new IllegalArgumentException("Unknown category '" + catStr + "' !  "))
      }
    
      def main(args: Array[String]) {
        BatchCategory.values.foreach(v => println(v + " isOfficial=" + v.isOfficial))
      }
    }
    

    отпечатков

    OFFICIAL isOfficial=true
    TEST isOfficial=false
    USER isOfficial=false
    

    Это было сделано для некоторого унаследованного кода, который нельзя переместить в более разумную стратегию enum, кроме Enumeration.

    ответ дан sourcedelica, с репутацией 20786, 28.05.2013
  • 1 рейтинг

    После проверки исходного кода Scala. Перечень, я получил это:

    
    object MyEnum extends Enumeration {
      val ONE = new Val { def method = "1" }
      val TWO = new Val { def method = "2" }
      val THREE = new Val { def method = "3" }
    }
    

    Кажется, трудно избавиться от «нового», так как используется анонимный класс. Если кто-нибудь знает, как это сделать, дайте мне знать :)

    ответ дан Shiva Wu, с репутацией 610, 6.12.2010
  • 0 рейтинг

    Ответ , в котором говорится, что перечисления Scala не поддерживают значения, настроенные для аргументов / методов, , кажется неправильным. Другие ответы (некоторые из них включают implicit) демонстрируют, что это возможно, но они создают впечатление, что требует дублирования имен: ваше значение объявлено как поле объекта java, и, во-вторых, имя передается конструктору значения как строка, тогда как вся точка из Enums состоит в создании итерируемой карты имен и значений, и Scala может обойтись без избыточности:

    object Ops1 extends Enumeration {
    
        protected case class OpsVal(f: Int => Int) extends super.Val(/*nextId*/)
    
        val ZERO = new FuncVal (x => 0)
        val DOUBLE = new FuncVal (x => 2 * x )
    
        implicit def convert(v: Value) = v.asInstanceOf[OpsVal]
    
    }
    
    // implicit is not needed
    Ops1.ZERO.f(1)                            //> res0: Int = 0
    
    // implicit is needed
    Ops1.values map (v => (v + "=>" + v.f(1)))//> res1: scala.collection.immutable.SortedSet[String] = TreeSet(DOUBLE=>2, ZERO=>0)
    

    Я думаю, что выше, является более кратким, чем

    object Ops2 extends Enumeration {
    
        protected abstract class OpsVal extends Val() {
          def f(a: Int): Int
        }
    
        val ZERO = new OpsVal { def f(x: Int) = 0 }
        val DOUBLE = new OpsVal { def f(x: Int) = 2 * x }
    
        implicit def convert(valu: Value) = valu.asInstanceOf[OpsVal]
    }
    Ops2.ZERO.f(1) // implicit is not needed  //> res2: Int = 0
    
    // implicit is needed
    Ops2_3.values map (v => (v, v(1)))        //> res7: scala.collection.immutable.SortedSet[(e.Ops2_3.Value, Int)] = TreeSet
                                                  //| ((ZERO,0), (DOUBLE,2))
    

    Поскольку для каждого значения существует один метод, мы можем преобразовать их в функции

    object Ops2_3 extends Enumeration {
    
        protected case class FuncVal(f: Int => Int) extends Val {
            def apply(x: Int) = f(x) // no need to extend Function1 explicitly
        }
    
        val ZERO = new FuncVal (x => 0)
        val DOUBLE = new FuncVal (x => 2 * x )
    
        implicit def convert(v: Value) = v.asInstanceOf[FuncVal]
    
    }
    Ops2_3.ZERO(1) // implicit is not needed  //> res6: Int = 0
    
    // implicit is needed
    Ops2_3.values map (v => (v, v(1)))        //> res7: scala.collection.immutable.SortedSet[(e.Ops2_3.Value, Int)] = TreeSet
                                                  //| ((ZERO,0), (DOUBLE,2))
    

    Функции, общие для всех значений, могут быть определены следующим образом (используется в arg parser )

    val args: Array[String] = "-silent -samples 100 -silent ".split(" +").toArray
                                                  //> args  : Array[String] = Array(-silent, -samples, 100, -silent)
    object Opts extends Enumeration {
    
        val nopar, silent, samples = new Val() {
            def apply() = args.contains(toString)
            def asInt(default: Int) = { val i = args.indexOf(toString) ;  if (i == -1) default else args(i+1).toInt}
            def asInt: Int = asInt(-1)
            override def toString = "-" + super.toString
        }
    }
    
    Opts.nopar()                              //> res0: Boolean = false
    Opts.samples.asInt                        //> res1: Int = 100
    

    Другие пользователи приводят доводы в пользу случаев запечатанных признаков + макросы, Итерации по запечатанным признакам в Scala?

    ответ дан Val, с репутацией 1, 19.07.2014