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

7 respostas
Rafael_Afonso

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,

7 Respostas

peczenyj

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

peczenyj

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

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

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

keller

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:

Rafael_Afonso

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

AkitaOnRails

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

A

Acredito que o Fabio Akita e peczenyj explicaram muito bem.

Rafael_Afonso

: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?

Criado 20 de novembro de 2007
Ultima resposta 22 de nov. de 2007
Respostas 7
Participantes 5