Apa arti dari @@ variabel di Ruby?

162

Apa variabel Ruby yang didahului dengan double at Sign ( @@)? Pemahaman saya tentang variabel yang didahului dengan tanda at adalah bahwa itu adalah variabel instan, seperti ini di PHP:

Versi PHP

class Person {

    public $name;

    public function setName($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

Setara dengan Ruby

class Person

    def set_name(name)
        @name = name
    end

    def get_name()
        @name
    end
end

Apa arti ganda pada tanda @@, dan bagaimana perbedaannya dari satu pada tanda?

Andrew
sumber
103
Saya tidak tahu, tapi saya merasa itu menatap saya. Saya agak takut untuk kode di Ruby sekarang ...
corsiKa
2
TL; DR untuk umum: 99 kali dari 100, saya akan menggunakan variabel "instance kelas" ( metode @di dalam self) bukan variabel kelas ( @@). Lihat litani alasan mengapa dalam jawaban di bawah ini.
WattsInABox

Jawaban:

240

Variabel awalan dengan @adalah variabel instan , sedangkan satu awalan dengan @@adalah variabel kelas . Lihatlah contoh berikut; outputnya ada di komentar di akhir putsbaris:

class Test
  @@shared = 1

  def value
    @@shared
  end

  def value=(value)
    @@shared = value
  end
end

class AnotherTest < Test; end

t = Test.new
puts "t.value is #{t.value}" # 1
t.value = 2
puts "t.value is #{t.value}" # 2

x = Test.new
puts "x.value is #{x.value}" # 2

a = AnotherTest.new
puts "a.value is #{a.value}" # 2
a.value = 3
puts "a.value is #{a.value}" # 3
puts "t.value is #{t.value}" # 3
puts "x.value is #{x.value}" # 3

Anda dapat melihat yang @@shareddibagikan di antara kelas-kelas; pengaturan nilai pada instance dari satu mengubah nilai untuk semua instance lain dari kelas itu dan bahkan kelas anak, di mana variabel bernama @shared, dengan satu @, tidak akan.

[Memperbarui]

Seperti yang disebutkan Phrogz dalam komentar, itu adalah idiom umum di Ruby untuk melacak data tingkat kelas dengan variabel instan pada kelas itu sendiri . Ini bisa menjadi subjek yang rumit untuk membungkus pikiran Anda, dan ada banyak bacaan tambahan tentang subjek, tetapi menganggapnya sebagai memodifikasi Classkelas, tetapi hanya contoh Classkelas yang Anda kerjakan. Sebuah contoh:

class Polygon
  class << self
    attr_accessor :sides
  end
end

class Triangle < Polygon
  @sides = 3
end

class Rectangle < Polygon
  @sides = 4
end

class Square < Rectangle
end

class Hexagon < Polygon
  @sides = 6
end

puts "Triangle.sides:  #{Triangle.sides.inspect}"  # 3
puts "Rectangle.sides: #{Rectangle.sides.inspect}" # 4
puts "Square.sides:    #{Square.sides.inspect}"    # nil
puts "Hexagon.sides:   #{Hexagon.sides.inspect}"   # 6

Saya menyertakan Squarecontoh (output mana nil) untuk menunjukkan bahwa ini mungkin tidak berperilaku 100% seperti yang Anda harapkan; yang artikel saya terkait di atas memiliki banyak informasi tambahan pada subjek.

Juga perlu diingat bahwa, seperti pada sebagian besar data, Anda harus sangat berhati-hati dengan variabel kelas dalam lingkungan multithreaded , sesuai komentar dmarkow.

Michelle Tilley
sumber
1
Jawaban ini akan menjadi IMHO sempurna jika Anda menyertakan kode yang menunjukkan bagaimana Anda dapat menggunakan variabel instan di tingkat kelas untuk melacak data tingkat kelas tanpa perilaku 'aneh' berbagi data antara subclass.
Phrogz
3
Saya juga menunjukkan bahwa variabel kelas dapat berbahaya / tidak dapat diandalkan dalam lingkungan multi-utas (mis. Rails)
Dylan Markow
Hmm ... dengan cara itu terdengar seperti variabel statis di PHP, tetapi bagian pewarisannya berbeda. Saya tidak berpikir PHP memiliki sesuatu yang persis seperti ini.
Andrew
5
Saya tidak mengerti apa yang dilakukan ruby class << self endblok, khususnya operator <<.
davidtingsu
1
untuk orang lain yang bingung class << selfmelihat ini
kapad
37

@- Variabel Instance dari suatu kelas
@@- Variabel kelas, juga disebut sebagai variabel statis dalam beberapa kasus

Variabel kelas adalah variabel yang dibagi di antara semua instance kelas. Ini berarti bahwa hanya satu nilai variabel yang ada untuk semua objek yang dipakai dari kelas ini. Jika satu instance objek mengubah nilai variabel, nilai baru itu pada dasarnya akan berubah untuk semua instance objek lainnya.

Cara berpikir lain untuk memikirkan variabel kelas adalah sebagai variabel global dalam konteks kelas tunggal. Variabel kelas dideklarasikan dengan mengawali nama variabel dengan dua @karakter ( @@). Variabel kelas harus diinisialisasi pada waktu pembuatan

Shaunak
sumber
10

@@ menunjukkan variabel kelas, yaitu itu dapat diwarisi.

Ini berarti bahwa jika Anda membuat subkelas dari kelas itu, itu akan mewarisi variabel. Jadi jika Anda memiliki kelas Vehicledengan variabel kelas @@number_of_wheelsmaka jika Anda membuatnya class Car < Vehiclemaka juga akan memiliki variabel kelas@@number_of_wheels

Fareesh Vijayarangam
sumber
Ini berarti bahwa jika Anda membuat subkelas dari kelas itu, itu akan mewarisi variabel. Jadi jika Anda memiliki kelas Vehicledengan variabel kelas @@number_of_wheelsmaka jika Anda membuatnya class Car < Vehiclemaka juga akan memiliki variabel kelas@@number_of_wheels
Fareesh Vijayarangam
12
Jika saya memiliki class Vehicledengan @number_of_wheels, maka class Car < Vehiclejuga akan memiliki variabel instan bernama @number_of_wheels. Perbedaan utama dengan variabel kelas adalah bahwa kelas memiliki variabel yang sama , misalnya mengubah satu mengubah yang lain.
Michelle Tilley
1

@ dan @@ dalam modul juga bekerja secara berbeda ketika kelas diperluas atau termasuk modul itu.

Jadi diberikan

module A
    @a = 'module'
    @@a = 'module'

    def get1
        @a          
    end     

    def get2
        @@a         
    end     

    def set1(a) 
        @a = a      
    end     

    def set2(a) 
        @@a = a     
    end     

    def self.set1(a)
        @a = a      
    end     

    def self.set2(a)
        @@a = a     
    end     
end 

Maka Anda mendapatkan output di bawah ini ditampilkan sebagai komentar

class X
    extend A

    puts get1.inspect # nil
    puts get2.inspect # "module"

    @a = 'class' 
    @@a = 'class' 

    puts get1.inspect # "class"
    puts get2.inspect # "module"

    set1('set')
    set2('set')

    puts get1.inspect # "set" 
    puts get2.inspect # "set" 

    A.set1('sset')
    A.set2('sset')

    puts get1.inspect # "set" 
    puts get2.inspect # "sset"
end 

class Y
    include A

    def doit
        puts get1.inspect # nil
        puts get2.inspect # "module"

        @a = 'class'
        @@a = 'class'

        puts get1.inspect # "class"
        puts get2.inspect # "class"

        set1('set')
        set2('set')

        puts get1.inspect # "set"
        puts get2.inspect # "set"

        A.set1('sset')
        A.set2('sset')

        puts get1.inspect # "set"
        puts get2.inspect # "sset"
    end
end

Y.new.doit

Jadi gunakan modul @@ in untuk variabel yang Anda inginkan umum untuk semua penggunaannya, dan gunakan modul @ in untuk variabel yang ingin Anda pisahkan untuk setiap konteks penggunaan.

Tantallion
sumber
1

Jawabannya sebagian benar karena @@ sebenarnya adalah variabel kelas yang per hierarki kelas artinya dibagi oleh kelas, instance dan kelas turunannya dan instance mereka.

class Person
  @@people = []

  def initialize
    @@people << self
  end

  def self.people
    @@people
  end
end

class Student < Person
end

class Graduate < Student
end

Person.new
Student.new

puts Graduate.people

Ini akan menampilkan

#<Person:0x007fa70fa24870>
#<Student:0x007fa70fa24848>

Jadi hanya ada satu variabel @@ yang sama untuk kelas Person, Mahasiswa dan Pascasarjana dan semua metode kelas dan instance dari kelas-kelas ini merujuk ke variabel yang sama.

Ada cara lain untuk mendefinisikan variabel kelas yang didefinisikan pada objek kelas (Ingat bahwa setiap kelas sebenarnya adalah instance dari sesuatu yang sebenarnya adalah kelas Kelas tetapi itu adalah cerita lain). Anda menggunakan @ notasi alih-alih @@ tetapi Anda tidak dapat mengakses variabel-variabel ini dari metode instan. Anda harus memiliki pembungkus metode kelas.

class Person

  def initialize
    self.class.add_person self
  end

  def self.people
    @people
  end

  def self.add_person instance
    @people ||= []
    @people << instance
  end
end

class Student < Person
end

class Graduate < Student
end

Person.new
Person.new
Student.new
Student.new
Graduate.new
Graduate.new

puts Student.people.join(",")
puts Person.people.join(",")
puts Graduate.people.join(",")

Di sini, @people adalah satu per kelas, bukan hierarki kelas karena sebenarnya merupakan variabel yang disimpan pada setiap instance kelas. Ini hasilnya:

#<Student:0x007f8e9d2267e8>,#<Student:0x007f8e9d21ff38>
#<Person:0x007f8e9d226158>,#<Person:0x007f8e9d226608>
#<Graduate:0x007f8e9d21fec0>,#<Graduate:0x007f8e9d21fdf8> 

Satu perbedaan penting adalah bahwa, Anda tidak dapat mengakses variabel kelas ini (atau variabel instance kelas yang dapat Anda katakan) langsung dari metode instance karena @people dalam metode instance akan merujuk ke variabel instance dari instance spesifik dari kelas Person atau Mahasiswa atau pascasarjana tersebut .

Jadi, sementara jawaban lain dengan benar menyatakan bahwa @myvariable (dengan notasi tunggal @) selalu merupakan variabel instan, itu tidak berarti bahwa itu bukan variabel tunggal yang dibagi untuk semua instance dari kelas itu.

Cagatay Kalan
sumber