Ruby: Memanggil metode kelas dari instance

347

Di Ruby, bagaimana Anda memanggil metode kelas dari salah satu instance kelas itu? Katakan saya punya

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

baris Truck.default_makemengambil default. Tetapi apakah ada cara untuk mengatakan ini tanpa menyebutkan Truck? Sepertinya harus ada.

Peter
sumber

Jawaban:

563

Alih-alih merujuk pada nama literal kelas, di dalam metode instance Anda hanya dapat memanggil 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

Output:

Metode kelas: Foo
Metode instance: Foo
Mark Rushakoff
sumber
7
saya ingin melihat beberapa jalan pintas di ruby ​​untuk memanggil metode kelas dari sebuah instance. yaitu:> some_class_method alih-alih self.class.some_class_method
phoet
7
sementara ini adalah jawaban yang tepat, sangat disayangkan bahwa "self.class" lebih mengetik dan kurang mudah dibaca daripada nama kelas "Truk". oh well ....
Matt Connolly
22
@MattConnolly, ini relatif, jika nama kelas Anda SalesforceSyncJoblebih pendek;)
drewish
29
@MattConnolly, juga menggunakan self.classmenghilangkan kebutuhan untuk mencari / mengganti jika Anda mengubah nama kelas.
Gus Shortz
8
@GusShortz benar. Juga, self.class berfungsi lebih baik jika ada subclass.
Matt Connolly
183

Menggunakan self.class.blahTIDAK sama dengan menggunakan ClassName.blahketika datang ke warisan.

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" 
Mark B
sumber
58
Ini tampaknya merupakan respons terhadap jawaban yang diterima daripada jawaban atas pertanyaan.
zhon
16
@ John - benar, tapi ini masih konteks yang berguna ketika mempertimbangkan apa yang akan digunakan.
Matt Sanders
1
@MattSanders hanya menggunakan komentar dalam kasus tersebut.
nandilugio
1
@hlcs self.classbenar untuk mempertahankan warisan. meskipun make1()didefinisikan dalam Truck, itu adalah BigTruckmetode kelas referensi .
Kaiser Shahid
Class_name.method_name berfungsi sempurna
Houda M
14

Untuk mengakses metode kelas di dalam metode contoh, lakukan hal berikut:

self.class.default_make

Berikut ini solusi alternatif untuk masalah Anda:

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

Sekarang mari kita gunakan kelas kita:

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

t = Truck.new
t.make
# => "Toyota"
t.year
# => nil
Harish Shetty
sumber
make seharusnya bukan metode instan. itu lebih seperti pabrik, yang harusnya terikat pada kelas daripada sebuah contoh
phoet
6
@phoet Kata make menunjukkan mobil (seperti di Toyota, BMW, dll.) englishforums.com/English/AMakeOfCar/crcjb/post.htm . Nomenklatur ini didasarkan pada kebutuhan pengguna
Harish Shetty
8

Jika Anda memiliki akses ke metode delegasi Anda dapat melakukan ini:

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

Atau, dan mungkin lebih bersih jika Anda memiliki lebih dari satu atau dua metode yang ingin Anda delegasikan ke kelas & contoh:

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

Sebuah kata peringatan:

Jangan hanya secara acak delegatesegala sesuatu yang tidak mengubah status ke kelas dan instance karena Anda akan mulai mengalami masalah nama clash yang aneh. Lakukan ini dengan hemat dan hanya setelah Anda memeriksa tidak ada lagi yang tergencet.

bbozo
sumber
6
self.class.default_make
yfeldblum
sumber
5

Anda melakukannya dengan cara yang benar. Metode kelas (mirip dengan metode 'statis' dalam C ++ atau Java) bukan bagian dari instance, jadi mereka harus dirujuk secara langsung.

Pada catatan itu, dalam contoh Anda, Anda akan lebih baik disajikan membuat 'default_make' metode biasa:

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

Metode kelas lebih berguna untuk fungsi tipe utilitas yang menggunakan kelas. Sebagai contoh:

#!/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
Jason Machacek
sumber
2
Saya tidak setuju bahwa itu default_makeharus menjadi metode instan. Sekalipun lebih sederhana untuk contoh-contoh ini, ini bukan semantik yang tepat - standarnya adalah produk kelas, bukan objek yang termasuk kelas.
Peter
1
@ Peter, apakah Anda mau menjelaskannya dengan istilah yang lebih sederhana? Saya baru belajar jawaban Ruby dan Maha sepertinya cocok untuk saya.
Marlen TB
1
@ MarlenT.B. melihat ke belakang, saya tidak yakin ada terlalu banyak yang bisa dipelajari di sini - saya hanya berdebat tentang di mana tempat terbaik untuk menempatkan metode ini, dan saya tidak membeli argumen saya sendiri sekuat lagi! :)
Peter
2
Saya juga tidak setuju. Apakah sesuatu adalah metode kelas tidak ada hubungannya dengan "utilitas". Ini tentang apakah metode secara konseptual berlaku untuk kelas, atau objek kelas itu. Misalnya, setiap truk memiliki nomor seri yang berbeda, jadi serial_number adalah metode instance (dengan variabel instance yang sesuai). Di lain vehicle_type (yang mengembalikan "truk") harus menjadi metode kelas karena itu adalah milik semua truk, bukan truk tertentu
vish
3

Satu lagi:

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
Alexey
sumber
1

Berikut ini adalah pendekatan tentang bagaimana Anda dapat menerapkan _classmetode yang berfungsi self.classuntuk situasi ini. Catatan: Jangan gunakan ini dalam kode produksi, ini hanya untuk kepentingan :)

Dari: Dapatkan Anda membuat kode dalam konteks pemanggil di Ruby? dan juga 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

Mungkin jawaban yang tepat adalah mengirim tambalan untuk Ruby :)

kapten
sumber
-6

Serupa dengan pertanyaan Anda, Anda dapat menggunakan:

class Truck
  def default_make
    # Do something
  end

  def initialize
    super
    self.default_make
  end
end
Daniel
sumber