Ruby:从实例调用类方法

在Ruby中,如何从类的实例中调用类方法?说我有

class Truck
def self.default_make
# Class method.
"mac"
end


def initialize
# Instance method.
Truck.default_make  # gets the default via the class's method.
# But: I wish to avoid mentioning Truck. Seems I'm repeating myself.
end
end

Truck.default_make检索默认值。但是有没有一种方法可以在不提到Truck的情况下这样说呢?似乎应该有。

227580 次浏览

你做得对。类方法(类似于c++或Java中的“静态”方法)不是实例的一部分,因此必须直接引用它们。

关于这一点,在你的例子中,你最好将'default_make'设置为常规方法:

#!/usr/bin/ruby


class Truck
def default_make
# Class method.
"mac"
end


def initialize
# Instance method.
puts default_make  # gets the default via the class's method.
end
end


myTruck = Truck.new()

类方法对于使用该类的实用程序类型函数更有用。例如:

#!/usr/bin/ruby


class Truck
attr_accessor :make


def default_make
# Class method.
"mac"
end


def self.buildTrucks(make, count)
truckArray = []


(1..count).each do
truckArray << Truck.new(make)
end


return truckArray
end


def initialize(make = nil)
if( make == nil )
@make = default_make()
else
@make = make
end
end
end


myTrucks = Truck.buildTrucks("Yotota", 4)


myTrucks.each do |truck|
puts truck.make
end

在实例方法中,你可以直接调用self.class.whatever,而不是引用类的字面名称。

class Foo
def self.some_class_method
puts self
end


def some_instance_method
self.class.some_class_method
end
end


print "Class method: "
Foo.some_class_method


print "Instance method: "
Foo.new.some_instance_method

输出:

Class method: Foo
Instance method: Foo

要访问实例方法中的类方法,请执行以下操作:

self.class.default_make

这里有一个解决你的问题的替代方案:

class Truck


attr_accessor :make, :year


def self.default_make
"Toyota"
end


def make
@make || self.class.default_make
end


def initialize(make=nil, year=nil)
self.year, self.make = year, make
end
end

现在让我们使用我们的类:

t = Truck.new("Honda", 2000)
t.make
# => "Honda"
t.year
# => "2000"


t = Truck.new
t.make
# => "Toyota"
t.year
# => nil
self.class.default_make

在继承方面,使用self.class.blah与使用ClassName.blah是不一样的。

class Truck
def self.default_make
"mac"
end


def make1
self.class.default_make
end


def make2
Truck.default_make
end
end




class BigTruck < Truck
def self.default_make
"bigmac"
end
end


ruby-1.9.3-p0 :021 > b=BigTruck.new
=> #<BigTruck:0x0000000307f348>
ruby-1.9.3-p0 :022 > b.make1
=> "bigmac"
ruby-1.9.3-p0 :023 > b.make2
=> "mac"

如果你可以访问委托方法,你可以这样做:

[20] pry(main)> class Foo
[20] pry(main)*   def self.bar
[20] pry(main)*     "foo bar"
[20] pry(main)*   end
[20] pry(main)*   delegate :bar, to: 'self.class'
[20] pry(main)* end
=> [:bar]
[21] pry(main)> Foo.new.bar
=> "foo bar"
[22] pry(main)> Foo.bar
=> "foo bar"

或者,如果你有多于一个或两个你想委派给类&实例:

[1] pry(main)> class Foo
[1] pry(main)*   module AvailableToClassAndInstance
[1] pry(main)*     def bar
[1] pry(main)*       "foo bar"
[1] pry(main)*     end
[1] pry(main)*   end
[1] pry(main)*   include AvailableToClassAndInstance
[1] pry(main)*   extend AvailableToClassAndInstance
[1] pry(main)* end
=> Foo
[2] pry(main)> Foo.new.bar
=> "foo bar"
[3] pry(main)> Foo.bar
=> "foo bar"

提醒一句:

不要只是随机delegate所有不改变类和实例状态的东西,因为你会开始遇到奇怪的名称冲突问题。这样做是有节制的,只有在你检查没有其他压扁。

下面是一种方法,介绍如何在这种情况下实现作为self.class工作的_class方法。注意:不要在产品代码中使用它,这是出于兴趣考虑:)

来自:你能在Ruby调用的上下文中评估代码吗?http://rubychallenger.blogspot.com.au/2011/07/caller-binding.html

# Rabid monkey-patch for Object
require 'continuation' if RUBY_VERSION >= '1.9.0'
class Object
def __; eval 'self.class', caller_binding; end
alias :_class :__
def caller_binding
cc = nil; count = 0
set_trace_func lambda { |event, file, lineno, id, binding, klass|
if count == 2
set_trace_func nil
cc.call binding
elsif event == "return"
count += 1
end
}
return callcc { |cont| cc = cont }
end
end


# Now we have awesome
def Tiger
def roar
# self.class.roar
__.roar
# or, even
_class.roar
end
def self.roar
# TODO: tigerness
end
end

也许正确的答案是为Ruby提交一个补丁:)

一个:

class Truck
def self.default_make
"mac"
end


attr_reader :make


private define_method :default_make, &method(:default_make)


def initialize(make = default_make)
@make = make
end
end


puts Truck.new.make # => mac

类似于你的问题,你可以用:

class Truck
def default_make
# Do something
end


def initialize
super
self.default_make
end
end