为什么`String`的子类上的`*`返回接收者的类?

时间:2015-02-10 23:26:15

标签: ruby string

如果我创建String的子类,*将返回该类的实例。

class MyString < String
end

MyString.new("foo").*(2).class #=> MyString

这与+%等其他类似操作不同,后者会返回String个实例。

MyString.new("foo").+("bar").class #=> String
MyString.new("%{foo}").%(foo: "bar").class #=> String

为什么*的行为与+%不同?

1 个答案:

答案 0 :(得分:1)

不满意的答案是......因为它是以这种方式实现的。

以下为the implementationString#+Rubinius

def +(other)
  other = StringValue(other)
  Rubinius::Type.compatible_encoding self, other
  String.new(self) << other
end

正如您所看到的,它明确地构建了一个String,这就是它总是返回String的原因。

以下是Rubinius中the implementationString#%

def %(args)
  *args = args
  ret = Rubinius::Sprinter.get(self).call(*args)

  ret.taint if tainted?
  return ret
end

它使用Rubinius::Sprinter格式化StringString始终返回def *(num) num = Rubinius::Type.coerce_to(num, Integer, :to_int) unless num.kind_of? Integer if num.kind_of? Bignum raise RangeError, "bignum too big to convert into `long' (#{num})" end if num < 0 raise ArgumentError, "unable to multiple negative times (#{num})" end str = self.class.pattern num * @num_bytes, self return str end

最后,这是Rubinius的the implementation String#*

String.pattern

这里有趣的部分是,它试图通过调用self.class.pattern而不是通过调用MyString::pattern来查找类方法,因此它实际上会调用String::patternString* s = state->new_object_dirty<String>(as<Class>(self)); // ^^^^^^^^^^^^^^^ 实际上是以primitive实现的,我不是很熟悉,但我相信有趣的是this

RubyString result = new RubyString(context.runtime, getMetaClass(), bytes);
//                                                  ^^^^^^^^^^^^^^

JRuby中,我认为神奇的是here

String

有趣的是,在Topaz中,该课程为MyString,而不是topaz -e 'class MyStr < String; end; p MyStr.new.*(2).class' # => String

  def mul(self, space, storage, times):
      return space.newstr_fromchars(self.unerase(storage) * times)

已实施here(我认为):

String#*

事实证明,ISO Ruby Language Specification可以说String

  

创建类String的直接实例 S ,其内容 C 重复 n 次。

注意它是如何表示“直接实例”,即不是String的子类的实例,而是String#*本身的实例。所以,乍一看,似乎YARV,JRuby和Rubinius违反了规范。但是,此规范适用于MyString#*,而不适用于String#*,因此问题是:如果方法由子类继承,此规范是否也适用?子类是否负责维护规范中的不变量,或者String是否有责任维护不变量,即使String#*是子类但{{1}}不是覆盖?