2016-05-17 1 views
2

Я хотел бы знать, есть ли способ создать класс перечисления из набора (например, набор строк).Scala: Создать объект перечисления из набора

Ниже приведен простая попытка:

val s = Set("v1", "v2", "v3") 

object sEnum extends Enumeration { 
    for (v <- s) { 
    val v = Value 
    } 
} 

for (v <- sEnum.values) { 
    println(v) 
} 

Объект перечисления кажется компилировать, но это значение шаткое: цикл печати показывает, что значения внутри sEnum выглядеть следующим образом:

< Недопустимое перечисление: нет поля для # 0>

Таким образом, проблема заключается в объявлении значения

val v = Value 

Есть ли способ заменить переменную v на ее содержимое во время выполнения (используя отражение, которое я предполагаю)?

ответ

0

TL; DR: Посмотрите на пример внизу.

Значения в вашем перечислении не имеют имен строк (хотя у них есть уникальные числовые идентификаторы). Есть два способа, как значение перечисления может получить имя строки:

  1. Вы передать его в явном виде при вызове Value, например, Value("v1")
  2. Если вы создаете поле val в классе, Scala передает значение из имени поля, например. в val v1 = Value. Это делается внутренне посредством отражения, изучая все методы 0-args, которые возвращают соответствующий тип.

Второй случай не относится к вашему примеру: val v = Value не создает метод на классе, v только локальная переменная внутри цикла (скрытие итерации переменной v). Таким образом, вы должны принять первый подход:

val s = Set("v1", "v2", "v3") 
object MyEnum extends Enumeration { 
    s.foreach(Value(_)) 
} 
MyEnum.values.foreach(println) // prints v1 v2 v3 

Теперь, это не позволяет получить доступ к значению перечислений легко, MyEnum.v1 не будет работать. Нет способа динамически добавлять методы/поля в класс на основе набора строковых значений. По крайней мере, не без магии манипулирования байт-кодами, где вы, вероятно, не хотите рисковать.

Вместо этого вы можете определить собственный метод для извлечения значений. Вот пример использования Symbols, так что вам не нужно использовать неэффективный метод MyEnum.withName():

val s = Set("v1", "v2", "v3") 

object MyEnum extends Enumeration { 
    type MyEnum = Value 
    s.foreach(Value(_)) 

    private val constants = s.map(v => Symbol(v) -> withName(v)).toMap 
    def apply(c: Symbol): MyEnum = constants(c) 
} 

MyEnum.values.foreach(println) // prints v1 v2 v3 
println(MyEnum('v1)) // prints v1 
Смежные вопросы