2012-04-30 10 views
42

Quello che voglio è:In Ruby, qual è la relazione tra "nuovo" e "inizializza"? Come restituire nil durante l'inizializzazione?

obj = Foo.new(0) # => nil or false 

Questo non funziona:

class Foo 
    def initialize(val) 
    return nil if val == 0 
    end 
end 

So in C/C++/Java/C#, non possiamo restituire un valore in un costruttore.

Ma mi chiedo se sia possibile in Ruby.

+12

Se si tratta di un controllo di integrità, una cosa più piacevole da fare sarebbe sollevare un'eccezione che spiega * perché * non si sta continuando l'inizializzazione. Restituire zero significa solo creare (serio) confusione. Nessuno si aspetterebbe mai che un oggetto appena istanziato sia zero;) – d11wtq

+0

Non ci sono costruttori in 'C'. – YoTengoUnLCD

+3

L'eccezione notevole alla dichiarazione di @ d11wtq sarebbe 'NilClass.new()'. –

risposta

5

È possibile qualcosa di simile:

class Foo 

    def self.init(val) 
    new(val) unless val == 0 
    end 

    def initialize(val) 
    #... 
    end 
end 

Esempio di utilizzo:

obj = Foo.init(0) 
=> nil 
obj = Foo.init(5) 
=> #<Foo:0x00000002970a98> 
+0

Nel tuo esempio, anche questa chiamata è nuova? – holaSenor

+0

Se si intende l'esempio quando chiamiamo 'init' con 0, quindi no,' new' non verrà richiamato. – Flexoid

42

Ci sono importanti differenze tra i due metodi.

new è una classe metodo , che crea in genere un'istanza della classe (questo si occupa la roba difficile come l'allocazione della memoria che Ruby si protegge da così non c'è bisogno di ottenere troppo sporca).

Poi, initialize, un'istanza metodo , dice dell'oggetto per impostare il suo stato interno secondo i parametri richiesti.

Uno di questi può essere ignorato a seconda di ciò che si desidera. Ad esempio, Foo.new potrebbe effettivamente creare e restituire un'istanza di FooSubclass se è necessario essere abbastanza intelligente per farlo.

Tuttavia, spesso è meglio delegare casi d'uso come questi ad altri metodi di classe che sono più espliciti su ciò che fanno, ad esempio Foo.relating_to(bar). Infrangere le aspettative di altre persone su ciò che dovrebbero fare metodi come new confonderà le persone più di quanto possa aiutarle a lungo termine.

Ad esempio, esaminare l'implementazione di Singleton, un modulo che consente l'esistenza di una sola istanza di una determinata classe. Rende privato il metodo new ed espone un metodo instance che restituisce l'istanza esistente dell'oggetto o chiama new se non è stato ancora creato.

71

In Ruby, qual è la relazione tra "new" e "initialize"?

new in genere chiama initialize. L'implementazione predefinita di new è qualcosa di simile:

class Class 
    def new(*args, &block) 
    obj = allocate 

    obj.initialize(*args, &block) 
    # actually, this is obj.send(:initialize, …) because initialize is private 

    obj 
    end 
end 

Ma si può, ovviamente, ignorare per fare tutto quello che vuoi.

Come restituire nil durante l'inizializzazione?

Quello che voglio è:

obj = Foo.new(0) # => nil or false 

Questo non funziona:

class Foo 
    def initialize(val) 
    return nil if val == 0 
    end 
end 

So in C/C++/Java/C#, non possiamo restituire un valore in un costruttore.

Ma mi chiedo se sia possibile in Ruby.

Non c'è cosa come un costruttore di in Ruby. I costruttori non sono necessari in un linguaggio ben progettato. In Ruby, ci sono solo metodi e ovviamente i metodi possono restituire valori.

Il problema che si sta vedendo è semplicemente che si desidera modificare il valore di ritorno di un metodo, ma si sta sovrascrivendo un metodo diverso. Ovviamente che non funziona. Se si desidera modificare il valore di ritorno del metodo bar, è necessario eseguire l'override di bar, non un altro metodo.

Se si desidera modificare il comportamento di Foo::new, allora si dovrebbe cambiare Foo::new:

class Foo 
    def self.new(val) 
    return nil if val.zero? 
    super 
    end 
end 

Si noti, tuttavia, che questo è un pessima idea, in quanto viola il contratto di new, che è quello di restituire un'istanza completamente inizializzata e pienamente funzionante della classe.

+0

"self.new" è corretto? Non dovrebbe essere "Foo.new'? – Kashyap

+2

'self' * è *' Foo' lì. Prova 'classe Foo; p auto fine # => Foo' –

4

Volendo fare

class Foo 
    def initialize(val) 
    return nil if val == 0 
    end 
end 

renderebbe il codice incoerente.

Se trovassi

class Foo 
    def initialize(val) 
    return nil if val == 0 
    @val = val 
    @bar = 42 
    end 
end 

cosa vorresti tornare se l'avete fatto Foo.new(1)? Vorresti 42 (il valore restituito per Foo#initialize) o un oggetto foo? Se si desidera un oggetto foo per Foo.new(1), perché si dovrebbe aspettare return nil per rendere nullo lo Foo.new(0)?

-1

Si è risolto semplicemente creando una variabile oggetto come questo:

class Foo 
    def initialize(val) 
     @val = val 
     return nil if @val == 0 
    end 
end 
obj = Foo.new(0) 

Output:- 
=>#<Foo:0x1243b8 @val=0> 

L'uscita varia in diversi computer.

2

Le classi in Ruby sono oggetti di prima classe - ognuno è un'istanza di classe Classe. Quando viene definita una nuova classe (in genere utilizzando Nome classe ... fine), un oggetto di tipo Classe viene creato e assegnato a una costante (Nome. In questo caso). Quando Name.new viene chiamato per creare un nuovo oggetto, il nuovo metodo di classe in Class viene eseguito per impostazione predefinita, che a sua volta richiama allocate per allocare memoria per l'oggetto, prima di chiamare definitivamente il metodo di inizializzazione del nuovo oggetto. Le fasi di costruzione e inizializzazione di un oggetto sono separate e entrambe possono essere superate. La costruzione viene eseguita tramite il nuovo metodo di classe, l'inizializzazione viene eseguita tramite il metodo di inizializzazione dell'istanza. inizializzare non è un costruttore!