Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby, class and inheritance

I need help understanding inheritance.

class MyArray < Array
end

a = MyArray[1, 2, 3] #=> [1, 2, 3]
b = MyArray[4, 5]    #=> [4, 5]
c = a + b            #=> [1, 2, 3, 4, 5]

a.class #=> MyArray
b.class #=> MyArray
c.class #=> Array

I don't understand why the result of the addition is not an instance of MyArray class.

like image 930
µgeek Avatar asked Jan 27 '26 15:01

µgeek


2 Answers

I don't understand why my array "a" is not "MyArray" class after add.

Why should it (be a MyArray)? Concatenation operation on arrays is defined to return a new Array, so that's what happens here. https://ruby-doc.org/core-2.5.3/Array.html#method-i-2B

If you want, you can override that operation in your class to return an instance of MyArray. Don't forget about all other similar methods.

This is also why it's a bad idea to subclass standard collections. Better to use composition over inheritance here.

like image 177
Sergio Tulentsev Avatar answered Jan 30 '26 12:01

Sergio Tulentsev


Just to add a bit to Sergio's answer in terms of his comment on using composition over inheritance and the exchange in the comments.

Instead of saying MyArray is an array you can say MyArrayLike has and array. Then you can "forward" methods that make sense to the underlying array but still add your own functionality that makes sense for your class without sub-classing array.

Ruby even has several ways to make this very easy including the Forwardable module.

class MyArrayLike 
   attr_reader :arr 
   def initialize( initial_arr )
      @arr = initial_arr 
   end

   def +(other)
     result = self.class.new(arr + other.arr)
     # maybe you want to do more than just concat the underlying array, if so you can do it here
     result
   end

   def first
      # for example maybe you want first to just return the first item in the underlying array.
      arr.first 
   end    
end


a = MyArrayLike.new([1,2,3])
b = MyArrayLike.new([4,5])

puts "a.class = #{a.class}"
# => a.class = MyArrayLike
puts a
# => #<MyArrayLike:0x00000000dc4b00>
a += b
puts "a.class = #{a.class}"
# => a.class = MyArrayLike
puts a 
# => #<MyArrayLike:0x00000000dc4470>

puts a.first 
# => 1
puts a.arr 
# => 1
#    2
#    3
#    4
#    5 
like image 21
nPn Avatar answered Jan 30 '26 11:01

nPn



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!