Ruby,类和继承

3

我需要帮助理解继承。

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

我不明白为什么加法的结果不是MyArray类的一个实例。

值得一读:小心子类化Ruby核心类 - Stefan
3个回答

5

我不明白为什么添加后我的数组"a"不是"MyArray"类。

为什么它应该是一个 MyArray 类型?数组的连接操作定义为返回一个新的Array数组,因此这就是在这里发生的。https://ruby-doc.org/core-2.5.3/Array.html#method-i-2B

如果您想要,可以在您的类中覆盖该操作以返回 MyArray 实例。但不要忘记所有其他类似的方法。

这也是为什么子类化标准集合的想法很糟糕。最好在这里使用组合而不是继承。


好的,我明白了。但是我想在我的对象MyArray上定义其他方法。我该怎么做? - µgeek
@µgeek:你是什么意思?就像定义其他任何方法一样。 - Sergio Tulentsev
谢谢您的建议。我会尝试更简单的练习... 将方法添加到标准类真的是一种不好的方法吗? - µgeek
1
“连接操作被定义为返回一个新的数组”,文档只是说“数组”(小写),没有指定类。而其他方法,如a[1..2]a * 2也返回一个“新数组”,但实际上仍保留了接收者的类,即它们确实返回MyArray实例。“为什么应该是MyArray?”因为这样更不会出乎意料。我认为转换回Array是由于优化而非面向对象编程的考虑。 - Stefan
"a[1..2] 或 a * 2 ... 返回 MyArray 实例" - 哇,完全没想到这个。 - Sergio Tulentsev
显示剩余5条评论

3

在Sergio的回答中,他提到了使用组合而非继承,并在评论中进行了交流。我想补充一点。

你可以说MyArrayLike 一个数组,而不是说MyArray 一个数组。然后,你可以“转发”那些对底层数组有意义的方法,同时为你的类添加自己的功能,而无需对数组进行子类化。

Ruby甚至有几种非常简单的方法来实现这一点,包括Forwardable模块。

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 

3
MyArray添加到MyArray中以获取Array可能有些反直觉,但可以定义一个方法来返回任何类。在调用Array#+的情况下,它恰好被定义为返回一个Array。就是这样。
如果你想让它返回一个MyArray,一种方法是定义MyArray#+如下:
class MyArray < Array
  def +other
    MyArray.new(super)
  end
end

(MyArray.new([1, 2, 3]) + MyArray.new([4, 5])).class # => MyArray

顺便提一下,注意你的MyArray#initialize定义是没有意义的,因此是多余的。


1
备选实现:dup.concat(other) - Stefan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接