Ответ 1
Если вам нужно перечисление java, вам нужно записать его на Java. Есть вещи, которые вы можете сделать в Scala для замены случаев использования Enum
, но в Scala ничего нет, что реплицирует механику Java Enum
.
Мое рабочее место экспериментировало при переходе с Java на Scala для некоторых задач, и оно хорошо работает для того, что мы делаем. Однако некоторые существующие методы ведения журнала ожидают a java.lang.Enum
. Метод ведения журнала определен в базовом классе (Java), и подклассы могут определять свои собственные перечисления, которые логгер будет отслеживать по всем экземплярам в нескольких потоках/машинах.
В Java это работает так:
public class JavaSubClass extends JavaBaseClass {
enum Counters {
BAD_THING,
GOOD_THING
}
public void someDistributedTask() {
// some work here
if(terribleThing) {
loggingMethod(Counters.BAD_THING)
} else {
loggingMethod(Counters.GOOD_THING)
// more work here
}
}
}
Затем, когда задача завершена, мы видим, что
BAD_THING: 230
GOOD_THING: 10345
Есть ли способ реплицировать это в Scala, создав Java Enum
или преобразовать из Enumeration
в Enum
? Я попытался расширить Enum
напрямую, но он кажется запечатанным, поскольку я получаю ошибку в консоли:
error: constructor Enum in class Enum cannot be accessed in object $iw
Access to protected constructor Enum not permitted because
enclosing object $iw is not a subclass of
class Enum in package lang where target is defined
Если вам нужно перечисление java, вам нужно записать его на Java. Есть вещи, которые вы можете сделать в Scala для замены случаев использования Enum
, но в Scala ничего нет, что реплицирует механику Java Enum
.
Перечисления Java
Для класса enum Counter
будет лучшее имя, чем Counters
- каждое значение перечисления представляет собой сингулярный счетчик.
Когда javac компилирует класс enum
, он:
Counter
), содержащий все конструкторы, методы, другие члены перечисления (если есть)каждое значение enum
(GOOD_THING
, BAD_THING
) получается полем public static
(1) - с классом, равным классу в (1) (Counter
):
// Java Code:
class Counter {
public static Counter GOOD_THING;
public static Counter BAD_THING;
// constructors, methods, fields as defined in the enum ...
}
логика инициализации в классе автоматически создает каждое значение enum
как одноэлементный объект
Scala Параметры
а. Ссылка Java Enum От Scala
Импортировать счетчик, обратитесь к GOOD_THING и BAD_THING, как в java, и (если хотите) дополнительно вызовите методы класса Enum:
// Scala Code:
import JavaSubClass.Counter;
def someDistributedTask = {
// some work here
if (terribleThing) {
loggingMethod(Counter.BAD_THING)
} else {
loggingMethod(Counter.GOOD_THING)
// more work here
}
}
// Other things you can do:
val GoodThing = Counter.valueOf("GOOD_THING")
Counter.values() foreach { // do something }
counter match {
case Counter.GOOD_THING => "Hoorah"
case Counter.BAD_THING => "Pfft"
case _ => throw new RuntimeException("someone added a new value?")
}
Преимущества: Может делать все, что перечисляет java, плюс поддерживает сопоставление шаблонов.
Недостатки: поскольку базовый признак не является sealed
, любой код, выполняющий сопоставление с образцом, не проверяется на типе, чтобы обеспечить исчерпывающие случаи.
В. Используйте Scala Перечисление
Преобразовать java enum
в эквивалент Scala Enumeration
:
// Scala Code:
object Counter extends Enumeration {
type Counter = Value
val GoodThing = Value("GoodThing")
val BadThing = Value("BadThing")
}
Используйте его:
// Scala Code:
import someScalaPackage.Counter;
def someDistributedTask = {
// some work here
if (terribleThing) {
loggingMethod(Counter.BadThing)
} else {
loggingMethod(Counter.GoodThing)
// more work here
}
}
// Other things you can do:
val GoodThing = Counter.withName("GoodThing")
val label = Counter.BadThing.toString
Counter.values foreach { // do something }
myCounter match {
case Counter.GOOD_THING => "Bully!"
case Counter.BAD_THING => "Meh"
case _ => throw new RuntimeException("someone added a new value?")
}
Преимущества: Scala Enumeration
методы такие же богатые, как Java enum
, плюс поддерживает сопоставление шаблонов.
Недостатки: не удается сделать все, что java enum
do - java enum определяется как класс с допустимыми конструкциями, методами и другими членами (т.е. Полное OO-моделирование по базовому типу enum). Поскольку базовый признак не является sealed
, любой код, выполняющий сопоставление с образцом, не проверяется на типе, чтобы гарантировать, что исчерпывающие случаи охвачены.
С. Используйте классы Scala Case:
Может преобразовать enum
непосредственно в объекты Case (т.е. объекты singleton, в отличие от Case Class, который не является одиночным):
sealed trait Counter
object Counter {
case object GoodThing extends Counter;
case object BadThing extends Counter;
}
Используйте его:
// Scala Code:
import someScalaPackage.Counter;
def someDistributedTask = {
// some work here
if (terribleThing) {
loggingMethod(Counter.BadThing)
} else {
loggingMethod(Counter.GoodThing)
// more work here
}
}
// Other things you can do:
// NO!! val GoodThing = Counter.withName("GoodThing")
val label = Counter.BadThing.toString
// NO!! Counter.values foreach { // do something }
myCounter match {
case Counter.GOOD_THING => "Bully!"
case Counter.BAD_THING => "Meh"
case _ => throw new RuntimeException("someone added a new value?")
}
sealed
, любой код, выполняющий сопоставление с образцом, проверяется на типе, чтобы гарантировать, что исчерпывающие случаи закрыты. (Не подходит для ваших требований).Хотя это, вероятно, не очень хорошая идея (см. другие сообщения для реальных хороших идей), можно расширить java.lang.Enum
в Scala. Ваш код сработал бы, если бы вы поместили как класс, так и его сопутствующий объект в один и тот же блок компиляции (в REPL каждый оператор выполняется в своем собственном модуле компиляции, если вы не используете режим :paste
).
Если вы используете режим :paste
и вставляете следующий код, Scala с радостью скомпилирует его:
sealed class AnEnum protected(name: String, ordinal: Int) extends java.lang.Enum[AnEnum](name, ordinal)
object AnEnum {
val ENUM1 = new AnEnum("ENUM1",0)
case object ENUM2 extends AnEnum("ENUM2", 1) // both vals and objects are possible
}
Однако взаимодействие с Java, вероятно, не будет удовлетворительным. Компилятор Java добавляет статические методы values
и valueOf
к новым классам enum
и гарантирует, что имена и ординалы верны, а Scala не будет.
Даже если вы предпримете эти шаги самостоятельно, Java не будет доверять вашему перечислению, потому что класс не имеет модификатора enum
. Это означает, что Class::isEnum
скажет, что ваш класс не является перечислением, что скажется, например, на статическом методе Enum::valueOf
. Оператор переключения Java также не будет работать с ними (хотя Scala соответствие шаблонов должно работать, если значения перечисления являются объектными объектами).
Как объясняется в этой ветке, у Дотти будет enum
для Scala 3.0 (середина 2020 года, семь лет спустя)
Scala также переработал
Enums
.
Они могут быть параметризованы и могут содержать пользовательские элементы.
object Day extends Enumeration {
type Day = Value
val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
}
//replaced with
enum Day {
case Mon, Tue, Wed, Thu, Fri, Sat, Sun
}
Из " Мартина Одерского - экскурсия по Scala 3 " (июнь 2019 года):
Перечисления могут быть параметризованы.
enum Day(val mon: Int) {}
Перечни:
- может иметь параметры
- может определять поля и методы
- может взаимодействовать с Java
enum Planet(mass: Double, radius: Double)
extends java.lang.Enum {
private final val G = 6.67300E-11
def surfaceGravity = G * mass / (radius * radius)
case MERCURY extends Planet(3.303e+23, 2.4397e6)
case VENUS extends Planet(4.869e+24, 6.0518e6)
case EARTH extends Planet(5.976e+24, 6.37814e6)
case MARS extends Planet(6.421e+23, 3.3972e6)
...
}
Перечисления могут иметь параметры типа, что делает их алгебраическими типами данных (ADT)
enum Option[+T] {
case Some(x: T)
case None
}
Перечисления компилируются в запечатанные иерархии классов дел и объектов.
sealed abstract class Option[+T]
object Option {
case class Some[+T](x: T) extends Option[T]
object Some {
def apply[T](x: T): Option[T] = Some(x)
}
val None = new Option[Nothing] { ... } }
}
Перечисления могут быть GADT (обобщенными ADT).
Таким образом, случаи могут расширять базовый тип аргументами другого типа.
enum Tree[T] {
case True extends Tree[Boolean]
case False extends Tree[Boolean]
case IsZero(n: Tree[Int]) extends Tree[Boolean]
case Zero extends Tree[Int]
case Succ(n: Tree[Int]) extends Tree[Int]
case If(cond: Tree[Boolean], thenp: Tree[T], elsep: Tree[T]) extends Tree[T]
}