NubyGems: Be Kind To Your Children

by Gregory Brown

Here's a little problem I ran into in some old code of mine.

Sometimes we've got methods where we want to return a new instance of the same class.

It's tempting to write the following code:


>> class A
>> def a_whole_new_me
>> A.new
>> end
>> end


Sure enough, that seems to work:


>> a = A.new
>> a.class
=> A
>> a.a_whole_new_me.class
=> A


So what's wrong with it? We forgot about subclasses!

>> class B < A; end
>> b = B.new
>> b.class
=> B
>> b.a_whole_new_me.class
=> A


If we're expecting a copy of B and get A, this is certainly going to cause trouble, but the scary part is it might not right away (since B usually has all of A's methods, but not necessarily the other way around)

Luckily, this is easy to fix, just use self.class

 
>> class A
>> def a_whole_new_me
>> self.class.new
>> end
>> end
>> a = A.new
>> a.class
=> A
>> a.a_whole_new_me.class
=> A
>> class B < A; end
>> b = B.new
>> b.class
=> B
>> b.a_whole_new_me.class
=> B


This practice is usually a good idea whenever we want to refer to our class object. Rather than making things rigid, if you use self.class when possible, your code will be easier to extend and behave better in general. Of course, your mileage may vary depending on your task.

6 Comments

Daniel Berger
2007-04-03 08:28:31
Indeed, this was recently addressed in the Pathname library. Good post. :)
Avdi
2007-04-03 10:29:56
Better yet, don't override the default #dup() method with a broken version. The #dup() method given above doesn't create a copy, it just creates a new blank instance.


The point about preferring "self" to an explicit classname is well taken, however. Definitely a best practice.

Gregory
2007-04-03 10:33:07
Well, it's a blank class.
So I don't think you'll find any difference. :)
barjak
2007-04-03 10:41:40
I agree with Daniel. #dup should not be overriden. Instead, the method #initialize_copy, which is called by #dup, is the method to override to add your custom behavior.
barjak
2007-04-03 10:43:36
Sorry, I meant "I agree with Avdi". The fact that the name of the author is below his comment is confusing.
Gregory
2007-04-03 10:49:46
#dup should not be overriden. Instead, the method #initialize_copy, which is called by #dup, is the method to override to add your custom behavior.


Good point, now that I look at the docs for that, I'll change the example to show a slightly different use case, but avoid this 'bad advice' mixing in with the good advice. :)