字符串(String)类型在程序语言中都有非常广泛的使用,因此字符串(String)也同整型、浮点型等一样被作为基本数据类型提供给开发者直接使用。在面向对象程序语言中,String作为对象的类,被赋予了各种方法来方便对String的操作和变换。而其他类,常常也会有默认的方法能让该类的实体对象转变成字符串(String)对象。比如最常见的就是Java和C#的toString()方法了。
将对象转换成字符串有几个好处:可以打印记录方便debug;可以存储为JSON或CSV等类型的文档又具有可读性。另一方面程序语言提供方法会在一些情况被隐式的调用来转换成字符串,比如对象和字符串拼接、String类型强制转换等等。开发者也可以重定义这些方法以得到希望的格式出现在上述情况中。
本文就来总结归纳一下当前流行的面向对象程序语言对“toString()”方法的实现方式。
Java和C#
这两种语言几乎是一对克隆兄弟,所以把他们放在一块。toString()也是这两种语言提供的将对象转换成String的系统方法。因为Java语言使用比较普遍,所以对于其他语言的实现方法也常常统称为“toString方法了”。
[codesyntax lang=”java” lines=”normal”]
public class HelloWorld { private String name; public HelloWorld(String n) { name = n; } @Override public String toString() { return "Hello world, " + name; } public static void main(String[] args){ HelloWorld hw = new HelloWorld("Ider"); String log = "[Java] " + hw; System.out.print(log); } } /* * Output: * [Java] Hello world, Ider */
[/codesyntax]
只要在新定义的类中,重载toString()方法,就可以在字符串拼接等操作中得到在toString()方法中定义的结果。
Objective-C
随着iPhone,iPad的流行以及iOS的兴起,Objective-C这门语言也越来越火热。Objective-C提供的字符串转换的方法是“– (NSString *)description”。这个名字并不是很好,因为常常会在数据库中会有一列也叫description来存储数据的备注。这样在ORM的时候就可能会出现问题了。
[codesyntax lang=”objc” lines=”normal”]
#import <Foundation/Foundation.h> @interface HelloWorld : NSObject @property (retain, nonatomic) NSString *name; @end @implementation HelloWorld -(NSString *)description { return [NSString stringWithFormat:@"Hello world, %@", self.name]; } @end int main(int argc, const char * argv[]) { @autoreleasepool { HelloWorld *hw = [[HelloWorld alloc] init]; hw.name = @"Ider"; NSLog(@"[Objective-C] %@", hw); } return 0; } /* * Output: * [Objective-C] Hello world, Ider */
[/codesyntax]
PHP
PHP作为脚本语言,其对象有一个特点是方法和属性不用预先在类中定义,在任何时候都可以通过赋值添加到对象上。但是为了可维护性,还是会遵从基本面向对象开发方式,把属性和方法都在类中定义。PHP提供了一些特殊的方法,这些方法都以两个下划线(__)为前缀,其中一个__toString()方法则是今天所关心的。
[codesyntax lang=”php” lines=”normal”]
<?php class HelloWorld { private $name; public function __construct($n) { $this->name = $n; } public function __toString() { return "Hello world, {$this->name}"; } } $hw = new HelloWorld('Ider'); echo '[PHP] ', $hw; /* * Output: * [PHP] Hello world, Ider */
[/codesyntax]
这里虽然没有用PHP的字符串连接运算符(.),但是因为echo方法接受的是String参数,所以它也会将类对象转换成String类型进而调用了神奇的__toString()方法。
JavaScript
JavaScript和Java的关系就像老婆和老婆饼一样,但是他们依然有很多的相似之处,比如它们都用toString()来实行类型到String的转换。不过JavaScript作为另一门脚本语言,同样是随时可添加属性和方法。
[codesyntax lang=”javascript” lines=”normal”]
var hw = {}; hw.name = 'Ider'; hw.toString = function() { return 'Hello world, ' + this.name; } console.log("[JavaScript] " + hw); /* * Output: * [JavaScript] Hello world, Ider */
[/codesyntax]
不同于基于Class的面向对象语言,这门基于prorptype的面向对象语言又有着非常特别的定义方式。
[codesyntax lang=”javascript” lines=”normal”]
function HelloWorld(n) { this.name = n; } HelloWorld.prototype.toString = function() { return 'Hello world, ' + this.name; }; var hw = new HelloWorld('Ider'); console.log("[JavaScript] " + hw); /* * Output: * [JavaScript] Hello world, Ider */
[/codesyntax]
所以要重载JavaScript的toString()方法就显得非常的多样和随意了。
Python
Python对于toString()的实现提供了两种方法:__repr__(self)和__str__(self)。这两种方法会在不同的情境下被调用,所以可以有选择的提供不同的实现。但是一般还是会希望两者保持一致,根据文档的说明当__str__(self)就会自动使用__repr__(self)。所以若为了方便,可以只定义__repr__(self)。
[codesyntax lang=”python” lines=”normal”]
class HelloWorld(object): def __init__(self, n): self.name = n def __repr__(self): return "Hello world, " + self.name + " (__repr__)" def __str__(self): return "Hello world, " + self.name + " (__str__)" hw = HelloWorld('Ider') print '[Python]', hw arr = ['[Python]', hw ] print arr # Output: # [Python] Hello world, Ider (__str__) # ['[Python]', Hello world, Ider (__repr__)]
[/codesyntax]
关于两者方法的具体区别,可参看引用处。
Lua
Lua是我最近刚刚接触的语言,也是除了JavaScript之外所见到的基于prototype的面向对象预言家,只不过它称为metatable。和其他语言一样,Lua也是提供了一个特殊的方法(metamethod)来实现“toString()”:__tostring。
[codesyntax lang=”lua” lines=”normal”]
HelloWorld = {}; function HelloWorld.new(n) local hw = { name=n }; setmetatable(hw, HelloWorld); return hw; end function HelloWorld.__tostring(this) return 'Hello world, ' .. this.name; end local hw = HelloWorld.new('Ider'); print ('[Lua]', hw); --[[ -- Output: -- [Lua] Hello world, Ider --]]
[/codesyntax]
C++
C++的标准库中提供的String类让饱受C那由字符数组构成的String折磨的人们得到了一定的解脱。C++作为最原始的面向对象语言,将面向对象思想引入到了开发之中,不过这可能也是导致C++的各种实现上有很多的不便利,比如它没有最基础的类是所有类的父类,也就没有什么通用的方法来重载了。但是通过C++的运算符重载方式,还是可以做到类似的toString()效果的,只是需要去实现的方法比较多。
[codesyntax lang=”cpp” lines=”normal”]
#include <iostream> #include <string> using namespace std; class HelloWorld { private: string name; public: HelloWorld(string n) { name = n; } string operator+(const string& other) { return (this->tostring() + other + " (plus)"); } operator string() { return this->tostring() + " (cast)"; } string tostring() { return "Hello world, " + name; } }; string operator+(const string &str, HelloWorld& hw) { return str+hw.tostring() + " (plus const)"; } ostream & operator<<(ostream &out, HelloWorld& hw) { out << hw.tostring() << " (ostream)"; return out; } int main(int argc, const char * argv[]) { HelloWorld hw("Ider"); string str = "[C++] "; cout << (str + hw) << endl; cout << (hw + " [C++]") << endl; cout << str << hw << endl; str += hw; cout << str << endl; return 0; } /* * [C++] Hello world, Ider (plus const) * Hello world, Ider [C++] (plus) * [C++] Hello world, Ider (ostream) * [C++] Hello world, Ider (cast) */
[/codesyntax]
从代码量上就看出来其实现要比其他语言要复杂的多,主要是C++没有一个方法可以处理所有情况,而必须依据不同情况来实现对应的方法。当然也可以只定义一个tostring()方法,而且在需要用的地方显示的调用方法就可以了。只是在某些模板(template)中可能就不能用了。
最后温馨提醒,千万不要在Google Images里搜C String哦。