Search code examples
crystal-lang

Crystal: inheritance vs inclusion vs extension


I asked a more limited version of this question at In Crystal, what's the difference between inheritance and inclusion?, but I think I (and other readers) would benefit from a more comprehensive answer.

What are the differences between inheritance (with <), inclusion (with include), and extension (with extend) in Crystal classes?


Solution

  • Answer

    Class inheritance

    Inheritance with < duplicates instance attributes, instance methods, class attributes, and class methods from the inherited class into the current class.

    Modules cannot inherit or be inherited.

    class A
      # Equivalent to `@@foo = 1` with corresponding getter and setter class methods
      @@foo = 1
    
      # Equivalent to `@foo = ` with corresponding getter and setter instance methods
      @foo = 2
    end
    
    class B < A
    end
    
    pp A.foo == B.foo  # true
    pp A.new.foo == B.new.foo  # true
    

    The value of a class attribute in the superclass is not the same as the value in the subclass, but the types will be the same.

    class C
      class_property foo = 1
    end
    
    class D < C
    end
    
    D.foo = 9
    pp C.foo == D.foo  # false
    

    A class can inherit from exactly one class.

    Module inclusion

    include duplicates instance attributes and instance methods from the included module into the current class or module.

    Classes cannot be included.

    It is possible to define class attributes and class methods on modules, but these are not duplicated by inclusion; they are ignored.

    module BirdHolder
      # Ignored by `include`
      class_property bird = "123"
      property bird = "456"
    end
    
    class Thing
      include BirdHolder
    end
    
    pp Thing.new.bird  # "456"
    pp Thing.bird  # Error: undefined method 'bird' for Thing1.class
    

    A type (module or class) can include any number of modules.

    Module extension

    extend duplicates instance methods from the included module into the current class or module, but as class methods.

    Classes cannot be extended.

    Class attributes and class methods from the extended module are ignored.

    module Talkative
      # Ignored by `extend`
      @@stuff = "zxcv"
    
      # Ignored by `extend`
      def self.say_stuff
        puts "stuff"
      end
    
      def say_stuff
        puts @@stuff
      end
    end
    
    class Thing
      extend Talkative
      @@stuff = "asdf"
    end
    
    Thing.say_stuff  # "asdf"
    

    Note that @@stuff in the definition of the Talkative module refers to the class attribute of the extending class, not to the class attribute of Talkative itself.

    It is not possible to extend a module that defines instance methods. This results in a compilation error.

    A type (module or class) can extend exactly one module.

    References

    This information is based on:

    This answer is valid as of Crystal 1.0.0