Вопрос по enums, java, scala, enumeration – stackoverflow.com/a/25923651/501113

37

a вы могли бы:

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?

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

https://github.com/rbricks/itemizedhttp://pedrorijo.com/blog/scala-enums/
Не ответ на ваш вопрос, но вы рассматривали возможность использования объектов case вместо перечислений, как обсуждалось здесь:stackoverflow.com/questions/1898932/... Ross
Я уже ответил на этот вопрос в (с несколькими вариантами) вstackoverflow.com/questions/4235250/... Ken Bloom
Проведя обширные исследования вариантов, я опубликовал гораздо более полный обзор этой области, включая общее решение шаблона «запечатанная черта + объект объекта», где я решил проблему упорядочения инициализации класса / объекта JVM:stackoverflow.com/a/25923651/501113 chaotic3quilibrium

Ваш Ответ

11   ответов
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.

1


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" }
}

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

3
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")
    }
  }
}
Подождите, в Перечислении вы должны написать A, B, C = Значение,stackoverflow.com/questions/11067396, Я не понимаю ваше расширение перечисления. Val
4

ачениям в вашем перечислении. С этим новым 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
     */
  }

} 
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.

Действительно хорошее решение! Cristian Vrabie
и теперь вы можете даже использовать неявный класс вместо class + implicit def: неявный класс SuitValue (...) {...} ozeebee
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 выражение.

Что такое ADT? mR_fr0g
Хотя это дает преимущество обеспечения исчерпывающего сопоставления с образцом, оно создает возможность человеческой ошибки, требуя явного определения целочисленного значения. Таким образом, если кто-то добавит кейс-объект Four, а затем расширит совпадение с кейс-регистром Four => "3", обратная карта не будет работать, поскольку теперь есть два кейс-объекта с одинаковым порядковым значением. chaotic3quilibrium
12

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

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

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

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

Можно ли «исследовать», какие объекты определены в другом объекте, посредством отражения во время выполнения?Как получить доступ к объектам внутри объекта, смешивая черту с отражением?

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

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

+1, да, перечисления в настоящее время немного странные. axel22
@soc: по крайней мере, когда они пишутscala.reflectим придется проверить, что все работает так, как должно. Ken Bloom
@ axel22: если бы они могли обойти ошибку # 4023, я бы наконец смог создатьEnum который сочетает в себе все преимущества подхода класса / объекта со всеми преимуществамиextends scala.Enumeration подход ... soc
Можно ли определить неявное приведение из конкретного перечисления к классу, который содержит метод? andrewmu
@Ken: Я надеюсь, что они исправят это раньше :-) soc
34

класса Enumeration.Val.

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) 
Вам также понадобитсяimport scala.language.implicitConversions подавить предупреждение компилятора. 200_success
Я не понимаю, в чем преимущество дублирования имени в аргументе конструктора? Разве перечисления не должны знать имена их ценностей? Официальный доктор говорит, что они делаютscala-lang.org/api/current/index.html#scala.Enumeration Val
Вы также можете расширить сsuper.Val безnextId а такжеname параметр. Вот так:protected case class Val(val mass: Double, val radius: Double) extends super.Val, Таким образом, вы можете создавать свои значения без имени значения, например:val Mercury = Val(3.303e+23, 2.4397e6) Jan van der Vorst
Проголосовал за одобрение редактирования, но ответ можно улучшить, продемонстрировав, как извлечь имя изval декларация. Patrick M
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

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)
Ницца! Добавление статических вспомогательных методов в окружающий класс выглядит как оптимальный подход с учетом текущих ограничений. soc
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))

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

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?

Проведя обширные исследования вариантов, я опубликовал гораздо более полный обзор этой области, включая решение шаблона «запечатанная черта + объект объекта», в котором я решил проблему упорядочения инициализации класса / объекта JVM:stackoverflow.com/a/25923651/501113 chaotic3quilibrium

Похожие вопросы