弱类型语言的优缺点是什么?

19 投票
7 回答
13169 浏览
提问于 2025-04-16 02:06

我非常喜欢PHP,这是一种类型比较弱的编程语言。弱类型的意思是你可以随时改变变量的类型,比如把一个数字变成字符串,这样的灵活性是它的一个优点。

不过,我在想,弱类型语言有什么缺点呢?像C这样的强类型语言有什么好处是PHP这种弱类型语言不能提供的?而且,如果我们使用类型设置(比如用double($variable)),也可以说弱类型语言可以像强类型语言一样工作。

所以,弱类型语言的好处我可能没提到的有哪些?更重要的是,它的缺点又是什么呢?

7 个回答

2

关于这个话题,已经写了很多书。这里面有一个固有的权衡;在弱类型语言中,很多烦恼就不复存在了。例如,在Python中,你根本不用担心把一个float(浮点数)和一个int(整数)相除;把一个int加到一个list(列表)里;也不用担心函数参数的类型(你知道吗,OCaml有专门的 +. 运算符来处理float的加法,因为普通的(+)运算符是用来处理int的!);还有就是忘记某个变量可能是空值……这些问题基本上都消失了。

但是,这样做又会带来一堆新的运行时错误:比如在Python中,[0]*5的结果是,等一下,[0,0,0,0,0]!而OCaml虽然强类型让人烦,但它的编译器能抓住很多很多的错误;这正是它的好处所在。这就是一种权衡。

9

我最近在使用强类型语言(比如Java)和弱类型语言(比如JavaScript)一段时间后,发现弱类型语言在做小项目时非常方便。不过,随着项目规模的扩大,管理起来就变得很困难了。你脑子里要记住的东西太多了,开始越来越依赖你的开发工具和编译器,否则编程就会变得很慢。这时候,强类型语言的优势就开始显现出来了,尤其是当应用程序变得非常庞大时。

有两个例子让我在使用弱类型JavaScript时特别抓狂,一个是使用外部库时,另一个是重构代码。

外部库:在强类型语言中,库里的代码本身就能自我说明。当我创建一个类型为Person的变量时,开发工具可以检查代码,告诉我有getFirstName()、getLastName()和getFullName()这些方法。但在弱类型语言中,变量可以是任何东西,函数的参数也可以是任何类型(这些都没有明确规定)。因此,开发者必须非常依赖文档、网上搜索、讨论论坛以及自己过去的记忆。我发现,在JavaScript中查找外部库的信息可能要花上好几个小时,而在Java中,我只需按一下“.”键,就能看到所有选项和相关文档。遇到那些文档不全的库时,使用弱类型语言会让人非常沮丧。最近我在使用jqplot这个相对知名但文档不完全的JavaScript库时,问自己“draw函数中的'plot'参数是什么?”我花了一个多小时翻阅源代码,最后还是放弃了,找了个替代方案。

重构代码:在强类型语言中,我可以快速重构,只需修改需要更改的文件,然后修复编译器报的错误。有些工具甚至可以一键重构。而在弱类型语言中,你得小心翼翼地搜索和替换,然后测试、测试、再测试,甚至还要再测试一次。尤其是在大型应用中,你很难完全确定自己是否找到了所有出错的地方并修复了它们。

对于简单的需求和小项目来说,这两个问题几乎不存在。但如果你在处理数十万行甚至数百万行代码的应用时,弱类型语言会让你抓狂。

我觉得很多开发者对此感到沮丧,并把它变成情绪化的讨论,原因在于我们有时会认为只有一种正确的方法。但每种方法都有其优缺点。一旦你意识到这一点,就能把情绪放在一边,选择最适合你当前需求的方式。

12

静态类型的一个好处是,很多错误在编译时就能被发现,而不会等到运行时才出现。比如,如果你有一个静态类型的类或接口作为函数的参数,那么你就不会不小心传入一个错误类型的对象(当然,前提是你没有故意进行错误的类型转换)。

当然,这并不意味着你不会传入一个正确类型但内容错误的对象,或者一个实现了接口但功能不对的对象。此外,如果你的代码覆盖率达到100%,比如PHP、Python等语言的使用者,那在编译时发现错误和运行时发现错误又有什么区别呢?

就我个人而言,我在使用静态类型语言和动态类型语言时都有过有趣的经历。通常这并不是决定性的问题,因为我从来没有在两种语言之间选择,除了它们的类型系统不同,通常还有其他更重要的事情需要考虑。我发现,当我使用静态类型语言时,我会故意“依赖编译器”,试图以一种方式编写代码,如果代码有问题,就无法编译。例如,有些重构可以通过在一个地方进行更改,然后修复所有产生的编译错误来完成,重复这个过程直到编译通过。而通过多次运行完整的测试套件来做到这一点可能并不太实际。不过,现在的集成开发环境(IDE)也能自动化在其他语言中的相同重构,或者测试运行得很快,所以这其实是一个方便性的问题,而不是可能性的问题。

那些在代码正确性方面有真正担忧的人,通常是在进行代码的形式证明。我个人的理解是,静态类型推导可以完成大部分(但不是全部)显式静态类型所做的工作,并且可以减少键盘的磨损。所以如果静态类型迫使人们以一种更容易证明的方式编写代码,那么从这个角度来看,它可能是有价值的。我说“如果”:我并不确定,而且大多数人其实也并不会证明他们的静态类型代码。

动态改变变量类型之类的事情

我觉得这并没有太大价值。总是很诱人去做一些像(Python/Django)这样的事情:

user = request.GET['username']
# do something with the string variable, "user"
user = get_object_or_404(User,user)
# do something with the User object variable, "user"

但实际上,在一个函数内使用相同的名字来表示不同的东西真的合适吗?也许吧,但可能不太合适。在静态类型语言中,像“重用”整数变量来表示其他东西并不是特别被鼓励。通常情况下,95%的时间里,不想想出简洁且描述性强的变量名的愿望不应该凌驾于代码清晰的需求之上……

顺便提一下,通常来说,类型意味着会发生隐式类型转换,而类型则意味着不会。根据这个定义,C语言在算术类型方面是弱类型的,所以我想这不是你想表达的意思。一般认为,完全的强类型反而更多是个麻烦,而“完全弱类型”(任何东西都可以转换为任何其他东西)在大多数语言中是没有意义的。所以这里的问题是,能容忍多少种隐式转换,才能让你的代码不至于变得难以理解。再比如在C++中,关于是否实现转换操作符和非显式单参数构造函数的争论就一直存在。

撰写回答