Thursday, October 22, 2009

Recipe 8.13. Calling a Superclass's Method










Recipe 8.13. Calling a Superclass's Method







Problem


When overriding a class's method in a subclass, you want to extend or decorate the behavior of the
superclass, rather than totally replacing it.




Solution


Use the super keyword to call the superclass implementation of the current method.


When you call super with no arguments, the arguments to your method are passed to the superclass method exactly as they were recieved by the subclass. Here's a Recipe class that defines (among other things) a cook method.



class Recipe
# … The rest of the Recipe implementation goes here.
def cook(stove, cooking_time)
dish = prepare_ingredients
stove << dish
wait_for(cooking_time)
return dish
end
end



Here's a subclass of Recipe that tacks some extra behavior onto the recipe. It passes all of its arguments directly into super:



class RecipeWithExtraGarlic < Recipe
def cook(stove, cooking_time)

5.times { add_ingredient(Garlic.new.chop) }
super
end
end



A subclass implementation can also choose to pass arguments into super. This way, a subclass can accept different arguments from its superclass implementation:



class BakingRecipe < Recipe
def cook(cooking_time, oven_temperature=350)
oven = Oven.new(oven_temperature)
super(oven, cooking_time)
end
end





Discussion


You can call super at any time in the body of a methodbefore, during, or after
calling other code. This is in contrast to languages like Java, where you must call super in the method's first statement or never call it at all. If you need to, you can even call super multiple times within a single method.


Often you want to create a subclass method that exposes exactly the same interface as its parent. You can use the *args constructor to make the subclass method accept any arguments at all, then call super with no arguments to pass all those arguments (as well as any attached code block) into the superclass implementation. Let the superclass deal with any problems with the arguments.


The
String#gsub
method exposes a fairly complicated interface, but the String subclass defined here doesn't need to know anything about it:



class MyString < String
def gsub(*args)
return "#{super} -- This string modified by
MyString#gsub (TM)"
end
end
str = MyString.new("Here's my string")
str.gsub("my", "a")
# => "Here's a string -- This string modified by MyString#gsub (TM)"

str.gsub(/m| s/) { |match| match.strip.capitalize }
# => "Here's MyString -- This string modified by MyString#gsub (TM)"



If the subclass method takes arguments but the superclass method takes none, be sure to invoke super with an empty pair of parentheses. Usually you don't have to do this in Ruby, but super is not a real method call. If you invoke super without parentheses, it will pass all the subclass arguments into the superclass implementation, which won't be able to handle them.


In the example below,
calling just super would result in an ArgumentError: it would pass a numeric argument into String#succ!, which takes no arguments:



class MyString
def succ!(skip=1)
skip.times { super() }
self
end
end

str = MyString.new('a')
str.succ!(3) # => "d"



Invoking super works for class
methods as well as instance methods:



class MyFile < File
def MyFile.ftype(*args)
return "The type is #{super}."
end
end

File.ftype("/bin") # => "directory"
MyFile.ftype("/bin") # => "The type is directory."














No comments:

Post a Comment