我需要帮助理解继承。
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
类的一个实例。我需要帮助理解继承。
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
类的一个实例。我不明白为什么添加后我的数组"a"不是"MyArray"类。
为什么它应该是一个 MyArray
类型?数组的连接操作定义为返回一个新的Array
数组,因此这就是在这里发生的。https://ruby-doc.org/core-2.5.3/Array.html#method-i-2B
如果您想要,可以在您的类中覆盖该操作以返回 MyArray 实例。但不要忘记所有其他类似的方法。
这也是为什么子类化标准集合的想法很糟糕。最好在这里使用组合而不是继承。
a[1..2]
或a * 2
也返回一个“新数组”,但实际上仍保留了接收者的类,即它们确实返回MyArray
实例。“为什么应该是MyArray?”因为这样更不会出乎意料。我认为转换回Array
是由于优化而非面向对象编程的考虑。 - Stefan在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
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
定义是没有意义的,因此是多余的。
dup.concat(other)
- Stefan