Ruby: llamando al método de clase desde la instancia


347

En Ruby, ¿cómo se llama a un método de clase desde una de las instancias de esa clase? Di que tengo

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

la línea Truck.default_makerecupera el valor predeterminado. Pero, ¿hay alguna manera de decir esto sin mencionarlo Truck? Parece que debería haberlo.

Respuestas:


563

En lugar de referirse al nombre literal de la clase, dentro de un método de instancia puede llamar 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

Salidas:

Método de clase: Foo
Método de instancia: Foo

77
Me gustaría ver un atajo en ruby ​​para llamar a un método de clase desde una instancia. es decir:> some_class_method en lugar de self.class.some_class_method
phoet

77
Si bien esta es la respuesta correcta, es una pena que "self.class" sea más tipeado y menos fácil de leer que el nombre de la clase "Truck". oh bueno ....
Matt Connolly

22
@MattConnolly, es relativo, si el nombre de su clase es SalesforceSyncJobentonces es más corto;)
sorteo

29
@MattConnolly, el uso también self.classelimina la necesidad de buscar / reemplazar si cambia el nombre de la clase.
Gus Shortz

8
@GusShortz cierto. Además, self.class funciona mejor si hay una subclase.
Matt Connolly

183

Usar self.class.blahNO es lo mismo que usar ClassName.blahcuando se trata de herencia.

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" 

58
Esto parece ser una respuesta a la respuesta aceptada en lugar de una respuesta a la pregunta.
zhon

16
@zohn: cierto, pero este sigue siendo un contexto útil cuando se considera qué usar.
Matt Sanders el

1
@MattSanders solo usa un comentario en esos casos.
nandilugio

1
@hlcs self.classes correcto para preservar la herencia. aunque make1()se define en Truck, hace referencia BigTruckal método de clase.
Kaiser Shahid

Class_name.method_name funciona perfecto
Houda M

14

Para acceder a un método de clase dentro de un método de instancia, haga lo siguiente:

self.class.default_make

Aquí hay una solución alternativa para su problema:

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

Ahora usemos nuestra clase:

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

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

make no debería ser un método de instancia. es más una especie de fábrica, que debería estar vinculada a la clase en lugar de una instancia
phoet

66
@phoet La palabra make denota la marca de un automóvil (como en Toyota, BMW, etc.) englishforums.com/English/AMakeOfCar/crcjb/post.htm . La nomenclatura se basa en los requisitos del usuario
Harish Shetty

8

Si tiene acceso al método delegado, puede hacer esto:

[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"

Alternativamente, y probablemente más limpio si tiene más de un método o dos que desea delegar a clase e instancia:

[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"

Una palabra de precaución:

No solo aleatoriamente delegatetodo lo que no cambia de estado a clase e instancia, porque comenzará a encontrarse con extraños problemas de choque de nombres. Haga esto con moderación y solo después de que haya verificado nada más se aplasta.



5

Lo estás haciendo de la manera correcta. Los métodos de clase (similares a los métodos 'estáticos' en C ++ o Java) no son parte de la instancia, por lo que deben ser referenciados directamente.

En esa nota, en su ejemplo, será mejor que haga que 'default_make' sea un método regular:

#!/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()

Los métodos de clase son más útiles para funciones de tipo de utilidad que usan la clase. Por ejemplo:

#!/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

2
No estoy de acuerdo con que default_makedebería ser un método de instancia. Incluso si es más simple para estos ejemplos, no es la semántica correcta: el valor predeterminado es un producto de la clase, no objetos que pertenecen a la clase.
Peter

1
@Peter, ¿te gustaría explicar eso en términos más simples? Solo estoy aprendiendo que las respuestas de Ruby y Maha me parecen perfectas.
Marlen TB

1
@ MarlenT.B. Mirando hacia atrás, no estoy seguro de que haya mucho que aprender aquí: solo estaba discutiendo sobre dónde era el mejor lugar para poner el método, ¡y ya no compro mi propio argumento con tanta fuerza! :)
Peter

2
Yo tampoco estoy de acuerdo. Si algo es un método de clase no tiene nada que ver con la "utilidad". Se trata de si el método se aplica conceptualmente a la clase, o un objeto de esa clase. Por ejemplo, cada camión tiene un número de serie diferente, por lo que serial_number es un método de instancia (con la variable de instancia correspondiente). Por otra VEHICLE_TYPE (que devuelve "camión") debe ser un método de clase, ya que es una propiedad de todos los carros, no un camión particular,
vish

3

Uno mas:

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

1

Aquí hay un enfoque sobre cómo implementar un _classmétodo que funcione self.classpara esta situación. Nota: No use esto en el código de producción, esto es por interés :)

De: ¿Puede evaluar el código en el contexto de una persona que llama en Ruby? y también 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

Quizás la respuesta correcta sea enviar un parche para Ruby :)


-6

Similar a su pregunta, podría usar:

class Truck
  def default_make
    # Do something
  end

  def initialize
    super
    self.default_make
  end
end
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.