在Ruby中,有没有一种方法可以重载Initialize构造函数?


74

在Java中,您可以重载构造函数:

public Person(String name) {
  this.name = name;
}
public Person(String firstName, String lastName) {
   this(firstName + " " + lastName);
}

Ruby中是否有办法实现相同的结果:两个带有不同参数的构造函数?

Answers:


79

答案是是和不是。

您可以使用多种机制来获得与其他语言相同的结果:

  • 参数的默认值
  • 可变参数列表(splat运算符)
  • 将参数定义为哈希

即使参数不同,该语言的实际语法也不允许您两次定义一个方法。

考虑到以上三个选项,可以通过以下示例实现

# As written by @Justice
class Person
  def initialize(name, lastName = nil)
    name = name + " " + lastName unless lastName.nil?
    @name = name
  end
end


class Person
  def initialize(args)
    name = args["name"]
    name = name + " " + args["lastName"] unless args["lastName"].nil?
    @name = name
  end
end

class Person
  def initialize(*args)
    #Process args (An array)
  end
end

您将在Ruby代码中经常遇到第二种机制,特别是在Rails中,因为它提供了两个方面的优势,并且允许一些语法糖来生成漂亮的代码,尤其是不必将传递的哈希值括在花括号中。

维基链接提供了更多的阅读


感谢您指出该Person.new(:first_name => "...", :last_name => "...")方法。出于某种原因,我并没有想到要使用它,但这确实回答了我的问题。
agentbanks217 2010年

5
Ruby 2.0开箱即用地
Felipe

27

我倾向于做

class Person
  def self.new_using_both_names(first_name, last_name)
    self.new([first_name, last_name].join(" "))
  end

  def self.new_using_single_name(single_name)
    self.new(single_name)
  end

  def initialize(name)
    @name = name
  end
end

但是我不知道这是否是最好的方法。


1
+1:不是最好的,但肯定很聪明。遵循Ruby的命名系统。
安德烈Leria

+1是个聪明的主意,尽管这并不是每个定义都重载。
Karthick S 2013年

4
class Person
  def initialize(name, lastName = nil)
    name = name + " " + lastName unless lastName.nil?
    @name = name
  end
end

3
class StatementItem
  attr_reader :category, :id, :time, :amount

  def initialize(item)
    case item
    when Order
      initialize_with_order(item)
    when Transaction
      initialize_with_transaction(item)
    end
  end

  def valid?
    !(@category && @id && @time && @amount).nil?
  end

  private
    def initialize_with_order(order)
      return nil if order.status != 'completed'
      @category = 'order'
      @id = order.id
      @time = order.updated_at
      @amount = order.price
    end

    def initialize_with_transaction(transaction)
      @category = transaction.category
      @id = transaction.id
      @time = transaction.updated_at
      @amount = transaction.amount
    end

end

1

您可以将double splat运算符 **与方法内部的逻辑或(双管道)结合使用||initialize以达到相同的效果。

class Person
  def initialize(**options)
    @name = options[:name] || options[:first_name] << ' ' << options[:last_name]
  end
end

james = Person.new(name: 'James')
#=> #<Person @name="James">

jill_masterson = Person.new(first_name: 'Jill', last_name: 'Masterson')
#=> #<Person @name="Jill Masterson">

但是,如果Person创建的新内容不带first_name,则添加<<操作将失败NoMethodError: undefined method '<<' for nil:NilClass。这是一种initialize处理这种情况的重构方法(strip如果排除了任何一个选项,则用于删除空格)。

class Person
  def initialize(**options)
    @name = options[:name] || [ options[:first_name] , options[:last_name] ].join(' ').strip
  end
end

goldfinger = Person.new(last_name: 'Goldfinger')
#=> #<Person @name="Goldfinger">

oddjob = Person.new(first_name: 'Oddjob')
#=> #<Person @name="Oddjob">

实际上,此方法可以处理Person.new不带参数或使用意外键的调用,以将@name设置为空字符串的新实例返回:

nameless = Person.new
#=> <#Person @name="">

middle_malcom = Person.new(middle_name: 'Malcom')
#=> <#Person @name="">

1

结帐功能红宝石,灵感来自Elixir模式匹配功能。

   class Person
     include Functional::PatternMatching

     defn(:initialize, String) { |name| 
       @name = name 
     }

     defn(:initialize, String, String) {|first_name, last_name| 
      @name = first_name + ' ' + last_name
     }
   end

1
这是从另一种语言借用实践的创新方法。我想看看你的代码示例更紧密地使用类似于原始海报的语言Personfirst_namelast_name
chemturion

0

您可以使用konstructor gem在Ruby中声明多个构造函数并模仿重载:

class Person
  def initialize(name)
    @name = name
  end

  konstructor
  def from_two_names(first_name, last_name)
    @name = first_name + ' ' + last_name
  end
end

Person.new('John Doe')
Person.from_two_names('John', 'Doe')
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.