【问题标题】:Advanced Java-like enums in RubyRuby 中的高级类 Java 枚举
【发布时间】:2010-11-30 11:36:06
【问题描述】:

首先,这不是Enums in Ruby的重复:)

该问题的公认答案表明这是在 Ruby 中表示枚举的好方法:

class Foo
  BAR = 1
  BAZ = 2
  BIZ = 4
end

在 Java 中,可以将多个值和方法附加到一个枚举值。我想在 Ruby 中实现相同或类似的目标。

表示这个 Java 枚举的最类似于 Ruby 的方式是什么:

public enum Enum

    VALUE_1("Value 1"),
    VALUE_2("Value 2"),
    VALUE_3("Value 3");

    Enum(String value) {
        this.value = value;
    }
    public String getValue() {
        return value;
    }
    private String value;
}  

编辑:

我还想保留 Java 枚举的隐含特性:

  • ...检索序数值
  • ...在枚举值(或等效的东西)上调用方法

示例:

Enum.VALUE_1.getValue(); // "Value 1"
Enum.VALUE_2.name();     // "VALUE_2"
Enum.VALUE_3.ordinal();  // 2

【问题讨论】:

  • 您应该删除构造函数上的“public”修饰符以使其编译。

标签: java ruby enums


【解决方案1】:
class MyEnum
  attr_accessor :value
  def initialize(value)
    @value = value
  end

  VALUE1 = new("Value 1")
  VALUE2 = new("Value 2")

  class << self
    private :new
  end
end

MyEnum::VALUE2 # Enum with value "Value 2"
MyEnum.new # Error

一个更精细的解决方案,允许您定义任意“枚举类”并为您提供ordinal()

def enum(*values, &class_body)
  Class.new( Class.new(&class_body) ) do
    attr_reader :ordinal

    def initialize(ordinal, *args, &blk)
      super(*args, &blk)
      @ordinal = ordinal
    end

    values.each_with_index do |(name, *parameters), i|
      const_set(name, new(i, *parameters))
    end

    class <<self
      private :new
    end
  end
end

# Usage:
MyEnum = enum([:VALUE1, "Value 1"], [:VALUE2, "Value 2"]) do
  attr_reader :str
  def initialize(str)
    @str = str
  end
end

MyEnum::VALUE1.str #=> "Value 1"
MyEnum::VALUE2.ordinal #=> 1

【讨论】:

  • 根据人们想要的行为,将其替换为attr_reader 可能是有意义的。但是我不明白为什么要完全删除它,因为我希望至少能够读取该值,否则一开始就没有任何意义。
  • 绝对正确,至少他应该可以做到: MyEnum::VALUE2.value ,或者我们可以覆盖 to_s 来返回值,这样会更简洁;)
  • 在该示例中,我将如何检索序数值(Java 中:VALUE1.ordinal())
  • 你不会的。为此,您要么必须将数字保存为第二个值,要么编写一个自动保留索引的解决方案。
  • 更新了我的问题更准确。
【解决方案2】:

您始终可以创建类似于 Java 版本的系统:

module Foo
  class Value
    attr_reader :value

    def initialize(value)
      # Save a frozen, immutable copy
      @value = value.dup.freeze
    end

    # Patch in methods to make it appear more friendly and string-like
    alias_method :to_s, :value
    alias_method :inspect, :value
  end

  # Define constants
  BAR = Value.new('bar')
  BAZ = Value.new('baz')
  BIZ = Value.new('biz')
end

puts Foo::BAR
# => bar

【讨论】:

    【解决方案3】:

    这是我建议的代码...

    EnumModule.rb

    module EnumModule
      CONVERT_PROC = Proc.new do
        @values = constants.collect{|c| const_get(c)}.freeze
    
        @values.each_with_index do |value, idx|
          the_symbol = constants.find{|c| const_get(c) == value}
          sig = class << value ; self end
          sig.send :define_method, :name, proc{the_symbol}
          sig.send :define_method, :ordinal, proc{idx}
    
          if value.is_a? Hash
            value.each do |k, v|
              sig.send :define_method, k, (v.is_a?(Proc) ? v : proc{v})
            end
          end
          value.freeze
        end
    
        class << self
          alias :value_of :const_get
        end
    
        module_function
        def each
          @values.each { |v| yield v }
        end
        def values
          @values
        end
        extend Enumerable
    
        freeze
      end
    
      def self.extended extending_obj
        extending_obj.module_eval &CONVERT_PROC
      end
    end
    

    SampleEnum.rb

    require 'EnumModule'
    
    module SampleEnum
      VALUE_1 = {
        to_s: 'Value_1_str',
        get_value: proc{'Value 1'}
      }
    
      VALUE_2 = {
        to_s: 'Value_2_str',
        get_value: proc{'Value 2'}
      }
    
      extend EnumModule
    end
    
    #defined method
    p SampleEnum::VALUE_1.get_value #=> "Value 1"
    p SampleEnum::VALUE_2.get_value #=> "Value 2"
    
    p SampleEnum::VALUE_1.to_s      #=> "Value_1_str"
    
    #name (returns the symbol of the constant)
    p SampleEnum::VALUE_1.name #=> :VALUE_1
    p SampleEnum::VALUE_2.name #=> :VALUE_2
    
    #ordinal
    p SampleEnum::VALUE_1.ordinal #=> 0
    p SampleEnum::VALUE_2.ordinal #=> 1
    
    #emulates Java Enum's valueOf(is an alias of const_get)
    p SampleEnum.value_of('VALUE_1').get_value  #=> "Value 1"
    p SampleEnum.value_of(:VALUE_1).get_value  #=> "Value 1"
    p SampleEnum.const_get('VALUE_1').get_value  #=> "Value 1"
    
    #emulates Java Enum's values
    SampleEnum.values.each do |m|
      p m.ordinal, m.name, m.get_value, m.to_s
    end
    
    #an Enumerable
    p SampleEnum.map{|m| m.get_value} #=> ["Value 1","Value 2"]
    

    通过扩展EnumModuleHash 常量的内容(键和值)成为单例方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-22
      • 2012-12-05
      相关资源
      最近更新 更多