We are on our way to understanding why Ruby has metaclasses. The short answer – TL;DR – is that metaclasses are necessary because Ruby implements class inheritance in a “flat way” – all classes derive from one class called (very plainly), Class. Class can’t hold all the methods that need to be inherited by the various classes in your program, so you need each class to have a special parent class – which is what the metaclass is.
Well, let’s get back to understanding all the details of class inheritance first. In our last lesson, we got to how we create “class instance variables.” Let us now figure out what the @@ notation is all about.
In the previous lesson, we started building up an animal hierarchy – starting with Animal, we can add Dogs, Cats, Humans and so on. When we define class instance variables for each animal, those variables are specific to that animal – Humans have 2 legs, and Dogs have 4.
Ruby lets you add something to this – a variable that is shared by all members of a hierarchy.
class Animal @@zoo_count=0 def initialize @@zoo_count += 1 end def zoo_size @@zoo_count end end class Dog < Animal; end # Assume the class has the same code as before class Cat < Animal; end # Assume this class has code that's similar to class Dog rover = Dog.new puts rover.zoo_size fluffy = Cat.new puts fluffy.zoo_size puts rover.zoo_size # The output will be 1, followed by 2, followed by 2
@@zoo_count is now available to all classes in the hierarchy below Animal. Notice how Rover also knows that the zoo size is 2, after Fluffy’s joined.
This is what the double-@ notation actually does – inserts a symbol into a binding that’s shared by all classes in a “sub-tree” of the class hierarchy. “Class variable” is a mis-nomer for this notation – a more reasonable name for it is a “family variable,” perhaps, or “community variable” because it’s available to Classes that are cousins of each other, rather than just to instances of a specific Class.