[Ruby - iniciante] Inicialização de atributos em classes

Olá:

Estou começando meus estudos no Ruby e me deparei com uma situação curiosa. Vejam o código abaixo:

irb(main):051:0> class MinhaClasse
irb(main):052:1>     @valor = 10
irb(main):053:1>     def get_valor
irb(main):054:2>         @valor
irb(main):055:2>     end
irb(main):056:1> end
=> nil
irb(main):057:0> m = MinhaClasse.new
=> #<MinhaClasse:0xc278b5>
irb(main):058:0> m.get_valor
=> nil

Ou seja, tenho uma class com atributo cujo valor é definido em sua declaração. Mas quando tento acessá-lo obtenho nil.
Agora uma alteração na minha classe:

irb(main):061:0> class MinhaClasse
irb(main):062:1>     @valor
irb(main):063:1>     def initialize(v)
irb(main):064:2>         @valor = v
irb(main):065:2>     end
irb(main):066:1>     def get_valor
irb(main):067:2>         @valor
irb(main):068:2>     end
irb(main):069:1> end
=> nil
irb(main):070:0> m = MinhaClasse.new(10)
=> #<MinhaClasse:0x1efa490 @valor=10>
irb(main):071:0> m.get_valor
=> 10

Por outro lado:

irb(main):072:0> class MinhaClasse
irb(main):073:1>     attr_accessor :valor 
irb(main):074:1> end
=> nil
irb(main):075:0> m = MinhaClasse.new
ArgumentError: Wrong # of arguments(0 for 1)
	from (irb):75:in `new'
	from (irb):75:in `binding'
	from C:/jruby-1.0.2/lib/ruby/1.8/irb.rb:150:in `evaluate'
	from C:/jruby-1.0.2/lib/ruby/1.8/irb.rb:150:in `eval_input'
	from C:/jruby-1.0.2/lib/ruby/1.8/irb.rb:70:in `signal_status'
	from C:/jruby-1.0.2/lib/ruby/1.8/irb.rb:147:in `eval_input'
	from C:/jruby-1.0.2/lib/ruby/1.8/irb.rb:70:in `each_top_level_statement'
	from C:/jruby-1.0.2/lib/ruby/1.8/irb.rb:146:in `loop'
	from C:/jruby-1.0.2/lib/ruby/1.8/irb.rb:146:in `catch'
	from C:/jruby-1.0.2/lib/ruby/1.8/irb.rb:146:in `eval_input'
	from C:/jruby-1.0.2/lib/ruby/1.8/irb.rb:70:in `start'
	from C:\jruby-1.0.2\bin\..\bin\jirb_swing:41:in `catch'
	from C:/jruby-1.0.2/lib/ruby/1.8/irb.rb:69:in `start'
	from C:\jruby-1.0.2\bin\..\bin\jirb_swing:41
irb(main):076:0> m = MinhaClasse.new(10)
=> #<MinhaClasse:0x162f16 @valor=10>

Então a moral da história seria: “Se sua classe possui atributos sempre inicialize-os no construtor. Não adianta inicializá-los na declaração pois eles sempre vão começar com nil.”? :?
Se é assim então por que no primeiro código já não é emitido nenhum tipo de aviso ou exceção? Se existe alguma forma de contornar essa situação, como é?

Grato,

Vc não pode pensar numa classe ruby como se fosse uma classe java.

Teste isso:

class Z (1..1000).each { puts "oi" } end

como esta unica linha de codigo não está dentro de nenhuma definição de método, ela será executada assim que vc declarar a classe :wink:

agora veja só

irb(main):052:0> x = class W irb(main):053:1> @valor=10 irb(main):054:1> end => 10 irb(main):055:0> puts x 10 => nil

interessante, não? então veja

irb(main):056:0> x = class W irb(main):057:1> @valor = @valor + 5 irb(main):058:1> end => 15 irb(main):059:0> x = class W irb(main):060:1> @valor = @valor + 50 irb(main):061:1> end => 65

e não para por ai:

irb(main):063:0> y = class W irb(main):064:1> @@x = 4 irb(main):065:1> end => 4 irb(main):066:0> y = class W irb(main):067:1> @@x = @@x * 2 irb(main):068:1> end => 8 irb(main):069:0> y = class W irb(main):070:1> z = 90 irb(main):071:1> end => 90 irb(main):072:0> y = class W irb(main):073:1> z = z / 3 irb(main):074:1> end NoMethodError: undefined method `/' for nil:NilClass from (irb):73 from ♥:0

Relendo o livro to Taq de ruby, na pagina 76-77 tem um exemplo mais ou menos assim:

[code]class X
@valor = 10
def get
@valor
end
def X.get
@valor
end
end

c = X.new
puts c.get # nil
puts X::get # 10[/code]

Segundo o livro: “variaveis de instância podem ser declaradas dentro de qualquer método da classe, porem se forem declaradas logo após a definição da classe, vc tem uma variável de instância de classe ( :hunf: ), que só pode ser acessada usando um método de classe”.

Assim:

class Numero
  @numero;
  def initialize(num=-1)
    @numero = num;
  end
  def get_num
    @numero
  end
end
numero = Numero.new
puts numero.get_num
numero = Numero.new(3)
puts numero.get_num

output:

-1
3

Porque você nao pode inicializar no construtor nao vejo o problema nisso… :slight_smile:
Espero ter respondido a sua duvida… :smiley:

Não seria uma perguntar em um forum de ruby? :stuck_out_tongue:
Boa sorte nos estudos… :thumbup:

peczenyj: É justamente o Livro do Taq que estou lendo!
keller: Concordo que um fórum de Ruby seria mais adequado.

Na prática, não use:

class X
  @numero = 10
  def get_numero
    @numero
  end

  def set_numero(valor)
    @numero = valor
  end

  def X.numero
    @numero
  end
end

O correto:

class X
  attr_accessor :numero

  @@numero = 20
  def initialize
    @numero = 10
  end

  def self.numero
    @@numero
  end
end

No caso de métodos de classe, ou use o prefixo self. ou faça o seguinte:

class X
  class << self
    def numero
      @@numero
    end
  end
end

Repitam o mantra: "getters e setters não tem utilidade em Ruby". Usem attr_accessor para a mesma função. Outro mantra: "não criem ambiguidades à toa, existe $, @ e @@ por uma razão".

Acredito que o Fabio Akita e peczenyj explicaram muito bem.

:shock: Opa! Me explique melhor isso! Através do operador "<<" você adicionou um método à variável class de X chamado numero que retorna uma variável de classe (estática?) que por acaso também se chama numero. É isso?