为什么可以在没有父类实例的情况下调用某些类和/或方法?

时间:2017-01-19 21:05:45

标签: ruby class

我接近代码学院Ruby轨道的终点,我对一件奇特的东西感到好奇:我的印象是一个类是常量,方法等的存储库。并且为了访问它们中的大多数,您首先需要创建该类的实例,或者在某些情况下可以调用它们自身的方法(因为它们在技术上都是全局对象的一部分)。然后我看到了这样的事情:

#Worked
Time.now

我理解为这是调用类[Time]实例的方法[now]。然后我尝试自己调用该方法:

#Failed
now

并且失败了,我假设虽然可以在一般范围[作为全局对象的一部分]创建方法,但是它依赖于" parent"的初始化变量。 class,它不能自己调用​​,因为它不知道搜索那些初始化变量的对象。之后我创建了一个测试类:

class Clock
  def initialize
    @hours = 1
    @minutes = 30
  end

  def showTime
    puts "The time is: #{@hours}:#{@minutes}"
  end
end

#this worked
watch = Clock.new
watch.showTime

#this failed
showTime

然后我创建了一个基本方法(假设它在全球范围内)

def mymethod
    puts "The mighty METHOD!"
end

#Works
mymethod

以我的方式调用此方法,而不引用全局对象。所以...我的问题如下:

  1. 如何以这种方式调用[Time.now]?不应该首先创建时间实例吗?
  2. 为什么我不能单独调用[now]方法?我是对的,它依赖于以这种方式调用时无法找到的资源吗?
  3. 为什么我不能单独调用showTime方法?但是,如果我在" global"上定义任何方法。级别我可以访问它而不引用全局对象

4 个答案:

答案 0 :(得分:4)

首先,你的直觉是正确的。

每个方法必须是某个接收器的实例方法。

全局方法被定义为Object类上的私有实例方法,因此似乎全局可用。为什么?从任何上下文Object始终在self的类层次结构中,因此Object上的私有方法总是可以在没有接收者的情况下调用。

def fuuuuuuuuuuun
end

Object.private_methods.include?(:fuuuuuuuuuuun)
# => true

类方法被定义为"单例类"上的实例方法。他们的类实例。 Ruby中的每个对象都有两个类,一个"单例类"使用实例方法只针对那个单个对象和一个"普通类"使用该类的所有对象的方法。类没有什么不同,它们是Class类的对象,可能有单例方法。

class A
  class << self # the singleton class
    def example
    end
  end
end

A.singleton_class.instance_methods.include?(:example)
# => true

定义类方法的其他方法是

class A
  def self.example
  end
end

# or 

def A.example
end

有趣的是,您可以使用相同的语法def (receiver).(method name)在任何对象(而不仅仅是类对象)上定义单例方法,如下所示

str = "hello"

def str.square_size
  size * size
end

str.square_size
# => 25

"any other string".square_size
# => raises NoMethodError

一些编程语言历史 - Singleton类取自Smalltalk语言,在那里它们被称为&#34;元类&#34;。基本上Ruby中的所有面向对象特性(以及Enumerable上的函数式枚举器)都取自Smalltalk语言。 Smalltalk是一种早期基于类的面向对象语言,创建于70年代。它也是发明图形用户界面的语言,如重叠窗口和菜单等。如果你喜欢Ruby也可以看看Smalltalk,你可能会再次坠入爱河。

答案 1 :(得分:2)

这称为类方法。如果CodeAcademy没有覆盖它,这是一个耻辱。以下是一些例子:

# basic way
class Foo
  def self.bar; :ok; end
end
Foo.bar # => :ok

# alternate syntax
class Foo
  class << self
    def bar; :ok; end
  end
end

# alternate syntax, if Foo class already exists
def Foo.bar; :ok; end

# alternate approach if Foo class already exists
Foo.class_exec do
  def bar; :ok; end
end

# to define a class method on an anonymous 'class' for a single instance
# you won't need to use this often
Foo.new.singleton_class.class_exec do
  def bar; :ok; end
end

# to define a class method on an instance's actual class
Foo.new.class.class_exec do
  def bar; :ok; end
end

获取类方法的另一种方法是extend一个模块。

module FooMethods
  def bar; :ok; end
end
module Foo
  extend FooMethods
end
Foo.bar # => :ok

请注意,对于Modules,方法始终定义为实例方法。这样,它们可以extended进入类范围,也可以included进入实例范围。模块也可以使用类方法,使用与上面显示的完全相同的语法/示例和类。但是,通过includeextend来加载模块的类方法并不容易。

答案 2 :(得分:1)

  

如何以这种方式调用[Time.now]?不应该有   首次创建时间实例?

Time.now方法是一个类方法,而不是实例方法,因此可以直接在Time类上调用而不是它的实例Time.new

使用self关键字在类本身上定义类方法:

class Time
  def self.now
    # code
  end
end

Time.now # works
  

为什么我不能单独调用[now]方法?我是对的   依赖于以这种方式调用时无法找到的资源?

当你自己打电话给方法&#34;&#34;你实际上隐含地在自己上调用它:

self.now

以上内容与以下相同:

now
  

为什么我不能自己调用​​方法showTime?但如果我定义   关于&#34;全球&#34;的任何方法级别我可以访问它而无需引用   全局对象

您在特定类上定义了showTime方法,因此您必须将该方法发送到该类。在&#34; global&#34;中定义方法时你在self隐式定义它的范围,后来对mymethod的调用实际上是self.mymethod所以它会起作用。

答案 3 :(得分:0)

Time.nowclass method

要定义类方法,您需要使用self.定义方法:def self.method_name

class Clock
  @hours = 1
  @minutes = 30

  def self.showTime
    puts "The time is: #{@hours}:#{@minutes}"
  end
end

Clock.showTime
#=> The time is: 1:30

如果您想自己致电now,可以在Time课程内完成:

class Time
  puts now
  #=> 2017-01-19 22:17:29 +0100
end