So I have multiple subclasses of Thing, represented here as ThingA and ThingB.
A few things should be taken for granted here:
def initialize( var = 'yes' )
@var = var
ThingB.new( var )
elsif self.class != ThingB
#code for ThingA
@Aness = 'huge'
#code for ThingA & ThingB
if self.class == ThingA
@var == 'yes'
class ThingA < Thing
class ThingB < Thing
ThingA.new( 'no' )
 pry(main)> ThingA.new
=> #<ThingA:0x60bd4b0 @Aness="huge", @var="yes">
#this is fine
 pry(main)> ThingB.new
=> #<ThingB:0x53ba6b8 @var="yes">
 pry(main)> ThingA.new( 'no' )
=> #<ThingA:0x64bec40 @var="no">
#this should be ThingB
I'm not necessarily advocating that this is the correct way to design your system, but there's two reasons that what you've written doesn't work as you intended it.
First, even in a 'simple' case, the above would never result in the return value being a ThingB; the last line of your
initialize method is a
puts call, and
puts always has a return value of
nil, so in the simple case of a 'normal' method, your return value still wouldn't be a ThingB instance, it'd be
But, as you say,
returnonly stops the first initialize from ending, and still returns the original object.
I'm assuming you mean using an explicit
return in the initialize method, like this hypothetical code:
class Thing def initialize( var = 'yes' ) @var = var if !self.verify? return ThingB.new( var ) # explicit return elsif self.class != ThingB #code for ThingA @Aness = 'huge' end #code for ThingA & ThingB puts 'END' end def verify? if self.class == ThingA @var == 'yes' else true end end end
So why doesn't that work? The answer is subtle, but ultimately simple, and pretty key to understanding Ruby (I think): you're not calling
initialize in your code, you're calling
new. New can't just return whatever initialize returns, because then your original class definition (without an explicit return) would have made ThingA.new return
new actually works is more like this:
class Thing def self.new(*args) obj = self.allocate obj.initialize(*args) # sort of; initialize is private return obj end end
You'll note that the return value of initialize is completely ignored; that's a good thing, if it weren't we'd have to have every initializer tediously return
self, and we'd get errors every time we forgot.
So, if you want
ThingA.new to return an instance of
ThingB, you don't need to modify
ThingA#initialize, you need to modify
class Thing end class ThingA < Thing def self.verify?(var) var == 'yes' end def self.new(var = 'yes') if self.verify?(var) super else ThingB.new(var) end end def initialize(var) @Aness = 'huge' end end class ThingB < Thing end
I should stress that this is not necessarily a wise thing to do with your code. But I do think knowing how to do it, and why it works, is important to understanding Ruby.
[*]: Again, not because it lacks an explicit return, but because it implicitly returns the value of the last evaluated expression, which is
puts 'END', and
puts always returns