With all these possible combinations of modules and classes, you may wonder how the compiler always finds the right method. This is the secret:
Each class (or type) has an ancestor list: a list of parent classes, starting with the superclass and ending with Object. The compiler looks up methods in this list, from the class itself to Object. If a method isn’t found, you’ll get a compile error like the following:
| error: undefined method 'method_name' for 'class_name' |
This is different from Ruby where you get a runtime NoMethodError.
What happens when modules are included in the class and a module has a method with the same name as a method in one of the parent classes? Modules are prepended into the ancestor list: the module’s method takes precedence. Take a look at what happens in the following example:
| module M1 |
| def meth1 |
| 41 |
| end |
| end |
| |
| class C1 |
| def meth1 |
| 42 |
| end |
| end |
| |
| class D1 < C1 |
| include M1 |
| end |
| |
| class E1 < D1 |
| end |
| |
| E1.new.m1 # => Error: undefined method 'm1' for E1 |
| E1.new.meth1 # => 41 # meth1 from module M1 is called |
meth1 from module M1 will be called, and not meth1 from class C1. If you write out the ancestor list, it appears as follows: E1 is the ancestor of D1 which is the ancestor of M1 which is the ancestor of C1 which is the ancestor of Object.
18.116.60.62