包括to_s和to_str、to_i和to_int、to_a和to_ary、to_h和to_hash。统称为to_x和to_xxx。

那么,to_x和to_xxx的区别是什么,什么时候使用to_x,什么时候使用to_xxx。

解释

使用鸭子模型来解释比较容易点。

只要像鸭子,就能当成鸭子,这就是to_x。只有它真的是鸭子,才能当成鸭子,这就是to_xxx。

以to_s和to_str为例。

所有对象都能使用to_s方法,用来将对象以字符串的格式去描述、去输出。也就是说,所有对象都能使用字符串的描述格式。
# 任意对象都能直接使用to_s()去描述自身 >> Object.new.to_s => "#<Object:0x00000002272e58>" #
数值类中重写了to_s(),使之转换成字符串格式的数值描述形式 >> 1.to_s => "1"
只有真的是字符串的对象,或者能完全扮演字符串的对象,才有必要去使用to_str
。例如,String类自身、String类的某些子类,它们是真的鸭子,并不是简单的像鸭子。也就是说,只有严格符合鸭子要求的类型,才可以考虑去定义to_str。

再严格一点,当某个地方能使用String类对象的时候,也一定能使用某类对象时(比如String的部分子类),这类对象就可以考虑去使用to_str。
>> 1.to_str NoMethodError: undefined method `to_str' for 1:Fixnum >>
Object.new.to_str NoMethodError: undefined method `to_str' for
#<Object:0x00000002267648>
或者说,to_x是输出出来给人读的,to_xxx是让程序健壮的,让你在不理解的情况下别乱定义to_xxx。

to_i和to_int、to_a和to_ary、to_h和to_hash也都一样,to_x是宽泛程度的数据类型转换,to_xxx
是严格的、必须知道是干什么的时候才进行的数据类型转换。



示例分析

例如:
>> [1, 2].join(',') => "1,2" >> [1, 2].join(1) TypeError: no implicit
conversion of Fixnum into String
数组的join()方法用来将数组转换成字符串,且使用连接字符进行连接。也就是说,数组中的每个元素以及连接符自身都得转换成字符串,才能保证转换的结果是字符串。

对于数组自身而言,调用to_s()
即可将其内所有元素转换成字符串格式,但是连接符不能随便转换,只有那些能够作为连接符的类型才能转换,例如这里的数值1不能作为连接符,所以应当让连接符的转换过程使用
to_str(),保证程序的健壮性、安全性。当然,如果你认为1也可以作为连接符,你可以在设计join()程序的时候,通过to_s()
去转换这里的数值1,但关键是join()不是你写的,而是别人写的,别人这么写有他自己的考虑。

再例如to_a和to_ary,将hash结构转换成array:
>> {a: 10}.to_a => [[:a, 10]] >> {a: 10}.to_ary NoMethodError: undefined
method `to_ary' for {:a=>10}:Hash Did you mean? to_a
上面第一个转换能成功。因为写hash类型的程序员认为,hash可以以一种方式转换成数组类型,于是它在hash类中定义了to_a()
。这个转换并不影响大局,仅仅只是实现一个简单的功能而已。

而to_ary()转换失败,因为hash是hash,array是array,在能使用array的地方,不代表能使用hash,假如在hash中定义了to_ary
,那么在很大意义上就意味着hash和array在很多地方可以互换使用(特指hash能替代array),也就是能使用array的地方很可能也应该允许它使用hash。当然,仅仅只是意义上的替换,而非真正的能替换,但这很可能会牵一发而动全身。

再例如,浮点数肯定可以使用to_i简单转化成整数类型,但它应该定义to_int()
吗?如果编写Float类的程序员认为,浮点数就是浮点数,绝不能当成int对象,那么他就要保证float对象不能转换成int,这时就不要定义to_int
。但如果他认为浮点数作为一种int使用,那么就应该定义to_int。事实上,Float类中to_i和to_int都定义了。
>> a=3.5 >> a.class # => Float >> a.to_i # => 3 >> a.to_int # => 3
结论

分为两种情况:什么时候调用to_x和to_xxx,以及什么时候在自己的类中实现to_x和to_xxx。

* 什么时候调用的问题
* 调用to_x来将你的类做个宽松的类型转换

* 调用Cls.to_xxx(arg)来验证arg真的能充当Cls使用

* 什么时候实现的问题
* 实现to_x,只要你认为可以按照你的观点转换将你的类转换成某个类型

* 实现Cls.to_xxx(arg),只有当前想要保证某arg对象真的可以充当Cls时定义
最后,基本上所有类都可以按照你自己的想法去定义to_x,但是很少定义to_xxx,除非你真的知道自己在干什么,知道这会造成什么结果。

参考链接:to_s vs. to_str (and to_i/to_a/to_h vs. to_int/to_ary/to_hash) in Ruby
<https://stackoverflow.com/questions/11182052/to-s-vs-to-str-and-to-i-to-a-to-h-vs-to-int-to-ary-to-hash-in-ruby>

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信