2013-12-05 2 views
11

Мое рабочее место экспериментирует с переходом с Java на Scala для некоторых задач, и оно хорошо работает для того, что мы делаем. Однако некоторые ранее существовавшие методы каротажа ожидают java.lang.Enum. Метод ведения журнала определен в базовом классе (Java), и подклассы могут определять собственные перечисления, которые логгер будет отслеживать по всем экземплярам в нескольких потоках/машинах.Создание Java-перечисления в Scala

как это работает в 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 s или преобразование с 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 
+0

Даже если вы переходите на scala, вы все равно можете написать java перечисления и использовать их из своего scala-кода. Scala не упрощает создание перечислений IMHO; запечатанная черта и набор объектов намного проще и, я считаю, предпочтительнее. – vptheron

+0

https://gist.github.com/viktorklang/1057513 – oluies

+0

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

ответ

15

Если вам нужен Java перечисление, то вам нужно записать его в Java. Есть вещи, которые вы можете сделать в Scala для замены прецедентовEnum, но в Scala нет ничего, что повторяет механику Java Enum.

+0

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

21

Java Перечисление

Для перечисления класса Counter будет лучше, чем именем Counters - каждое значение перечисления представляет исключительный счетчик.

Когда Javac компилирует enum класса, это:

  1. компилируется в обычный класс Java (например, Counter), содержащие все конструкторы, методы, другие членов перечисления (если таковой имеется)
  2. каждая 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 ... 
    
    } 
    
  3. инициализации логики в классе автоматически создает каждый 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, любой код, выполняющий сопоставление с образцом, не проверяется на печать, чтобы гарантировать, что исчерпывающие случаи закрыты.

B. Использование 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?") 
} 

Преимущества: Enumeration методы Scala является так богата, как Java Enum, плюс поддержка соответствия шаблону. Disadvanges: Невозможно сделать все, что java enum s do - java enum определены как класс с допустимыми конструкциями, методами и другими членами (т. Е. Полное OO-моделирование для базового типа перечисления). Поскольку базовый признак не равен sealed, любой код, выполняющий сопоставление с образцом, не проверяется на типизацию, чтобы обеспечить исчерпывающие случаи.

C. Использование Scala Case Классы:

может преобразовать enum s непосредственно в Case Objects (то есть одноплодная объекты, в отличие от случая класса, который не является синглтоном):

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?") 
} 
  • преимущество над исчислением : каждое значение может иметь разные предки или разные признаки микса (до тех пор, пока каждое значение соответствует типу Counter). Могут делать arbirtrarily сложное OO-моделирование для счетчика признаков и для каждого значения. Затем можно выполнить произвольно сложное сопоставление шаблонов, используя все различные параметры объекта case для каждого другого значения. Имея базовый признак sealed, любой код, выполняющий сопоставление с образцом, проверяется на соответствие, чтобы гарантировать, что исчерпывающие случаи закрыты. (Не подходит для ваших требований).
  • Недостаток над перечислением: не получайте методы перечисления «бесплатно» (т. Е. Значения, withName, application). Может быть «исправлена» путем добавления пользовательских реализаций к базовому классу Counter (немного для справки, поскольку это ручное кодирование ...).
+0

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

3

Хотя это, вероятно, не очень хорошая идея (см другие сообщения для реальных хороших идей), то является можно расширить 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 Interop, вероятно, не будет удовлетворительным. Компилятор Java добавляет статические методы values и valueOf к новым классам enum и гарантирует правильность названий и ординалов, которых нет у Scala.

Даже если вы сделаете эти шаги самостоятельно, Java не будет доверять вашему перечислению, потому что класс не имеет модификатора ENUM. Это означает, что Class::isEnum скажет, что ваш класс не является перечислением, что скажется, например, на статическом методе Enum::valueOf.Оператор switch Java не будет работать с ними (хотя соответствие шаблону Scala должно работать, если значения перечисления являются объектами case).

Смежные вопросы