2010-01-18 2 views
22

Класс-экземпляр класса ruby ​​дает мне головную боль. Я понимаю, с учетом этого ...Переменные класса Ruby

class Foo 
    @var = 'bar' 
end 

... что @var является переменным в экземпляре Созданного класса.

Но как создать переопределяемую переменную класса подкласса?

Вот пример того, что я хотел бы сделать в Python:

class Fish: 
var = 'fish' 
def v(self): 
    return self.var 

class Trout(Fish): 
    var = 'trout' 

class Salmon(Fish): 
    var = 'salmon' 

print Trout().v() 
print Salmon().v() 

Какие выходы:

trout 
salmon 

Как сделать то же самое в Ruby?

+1

не должен» t, который будет @@ var в первом блоке кода? – Jean

+0

Я читаю «Хорошо обоснованный рубист» (http://manning.com/black2/) Дэвида Блэка. Он отлично справляется с объяснением всех нюансов в этой области. –

+1

Jean: Нет. Если бы я использовал '@@ var', то подклассы переопределяли родительские классы. См. Ссылку hobodave. В частности, как «2» переоценивается в этом примере. –

ответ

8

@var упоминалось выше, называется переменная экземпляра класса, который отличается от переменных экземпляра ... читать ответ here, чтобы увидеть диф.

В любом случае это эквивалентно рубин код:

class Fish 
    def initialize 
    @var = 'fish' 
    end 

    def v 
    @var 
    end 
end 

class Trout < Fish 
    def initialize 
    @var = 'trout' 
    end 
end 

class Salmon < Fish 
    def initialize 
    @var = 'salmon' 
    end 
end 

puts Trout.new.v 
puts Salmon.new.v 
+0

На самом деле это не так, потому что вы должны переопределять инициализацию каждый раз, когда выполняете подкласс. Это не очень удобно. Возможно, в моем примере было лучше включить некоторый код в инициализацию. –

21

контрасту @ ответ khelll, это использует переменные экземпляра на объекты класса:

class Fish 
    # an instance variable of this Class object 
    @var = 'fish' 

    # the "getter" 
    def self.v 
    @var 
    end 

    # the "setter" 
    def self.v=(a_fish) 
    @var = a_fish 
    end 
end 

class Trout < Fish 
    self.v = 'trout' 
end 

class Salmon < Fish 
    self.v = 'salmon' 
end 

p Trout.v # => "trout" 
p Salmon.v # => "salmon" 

Edit: дать экземпляры Read- доступ к переменной экземпляра класса:

class Fish 
    def type_of_fish 
    self.class.v 
    end 
end 

p Trout.new.type_of_fish # => "trout" 
p Salmon.new.type_of_fish # => "salmon" 
+0

Нет, не совсем. Вы не делаете Trout.new или Salmon.new в конце. Вы используете сам класс. Я хочу, чтобы * экземпляр * получал переменные класса. –

+0

@ The Doctor - как насчет сейчас? –

+0

Yup. Я думаю, что теперь это функционально equiv. на ответ, который я дал. Вы просто пишете аксессуры вручную. –

2

Это распространенная ошибка, сделанная Java-кодонами, приходящими на Ruby, а также один из больших концептуальных прыжков, с которыми мне пришлось столкнуться. Сначала это кажется странным, но на самом деле это один из самых крутых аспектов Ruby - весь исполняемый код, включая определения классов.

Итак, переменные экземпляра должны быть объявлены внутри методов. Это связано с тем, как оценивается «я». «Я» - текущий объект. Интерпретатор будет искать вызовы методов и ссылки на переменные сначала в «я»:

class Fish 
    @var = "foo" # here 'self' == Fish, the constant which contains the class object 
    def foo 
     # do foo 
    end 
end 

fish = Fish.new 
fish.foo # here 'self' == fish, an instance of Fish 

В определении класса «я» устанавливаются, чтобы быть объектом класса определяется, таким образом, любые ссылки внутри определения класса будут относиться к этот объект класса, в данном случае Fish.

Однако, когда метод вызывается в экземпляре Fish, self устанавливается как получатель вызова, конкретный экземпляр Fish. Таким образом, вне определения метода self является объектом класса. Внутри метода self является экземпляром приемника. Вот почему @var вне определения метода больше похоже на статическую переменную в Java, а @var внутри определения метода - это переменная экземпляра.

+0

Typo: 'fish.foo' должно быть' fish.var' –

+0

Собственно, нет. Я не определял «foo», но дело в том, что интерпретатор Ruby увидит «рыбу» в качестве получателя вызова и установил «я», чтобы «ловить рыбу», чтобы разрешить ссылку. Однако я добавил метод foo для ясности. Вызов «fish.var» вызовет NoMethodError. –

+0

Ах. Виноват. Спасибо, что объяснили это. –

4

Вот версия, которую я в конечном итоге выяснить, используя ссылку hobodave в:

class Fish 
    class << self 
    attr_accessor :var 
    end 

    @var = 'fish' 
    def v 
    self.class.var 
    end 
end 

class Trout < Fish 
    @var = 'trout' 
end 

class Salmon < Fish 
    @var = 'salmon' 
end 

puts (Trout.new).v # => trout 
puts (Salmon.new).v # => salmon 

Обратите внимание, что подклассы требуют только не добавляя @var - нет необходимости переопределить инициализации.

1

Существует один вопрос: вы можете переопределить @var:
Salmon.var = 'акула' перекроет @var, так
путы (Salmon.new) .в # => акула

Смежные вопросы