静态类型和动态类型的语言有什么区别?


945

我听到很多新的编程语言是动态类型的,但是当我们说一种语言是动态类型还是静态类型时,这实际上意味着什么?


25
好问题,但您接受的答案不是正确答案。
乔恩·哈罗普

42
您能推荐一些正确的东西吗?
Sagiruddin Mondal

@EricLeschinski我认为单元测试现在可以解决该问题,并且可以放心地对诸如JavaScript之类的动态类型化语言进行编码,以确保它可以持久使用,从而使其有资格进行企业软件开发,您是不是认为呢?
像素67:

8
充其量,这些单元测试会随着时间的推移而恶化,并被试图提高工作安全性的同事关闭,最糟糕的是,它们从来没有被编写过。这就像建议专业技工在其客户的汽车上使用胶带。是的,初级,在您的传输工作中使用胶带是个好主意。
埃里克·莱斯钦斯基

2
我知道这个问题已有10年历史了,但是根据我在评论中看到的内容,您可能应该切换为接受“克里斯托弗·托卡”的答案。
Rev1.0

Answers:


853

静态类型语言

如果在编译时知道变量的类型,则语言是静态类型的。对于某些语言,这意味着您作为程序员必须指定每个变量的类型(例如:Java,C,C ++);其他语言提供某种形式的类型推断,即类型系统推断变量类型的能力(例如:OCaml,Haskell,Scala,Kotlin)

这里的主要优点是编译器可以完成所有类型的检查,因此在很早的阶段就捕获了许多琐碎的错误。

示例:C,C ++,Java,Rust,Go,Scala

动态类型语言

如果语言与运行时值关联,而不是命名为变量/字段/等,则该语言将被动态键入。这意味着您作为程序员可以更快地编写代码,因为您不必每次都指定类型(除非使用带有类型推断的静态类型语言))。

示例:Perl,Ruby,Python,PHP,JavaScript

大多数脚本语言都具有此功能,因为仍然没有编译器执行静态类型检查,但是您可能会发现自己正在寻找由于解释器错误解释变量类型而导致的错误。幸运的是,脚本通常很小,因此错误没有太多隐藏的地方。

大多数动态类型化的语言的确允许您提供类型信息,但不需要。目前正在开发的一种语言Rascal采用了一种混合方法,该方法允许在函数内进行动态键入,但对函数签名强制执行静态键入。


6
@NomeN您能命名任何实现HM类型推断的动态类型语言吗?
Pete Kirkham

88
“如果在运行时解释了变量的类型,则动态地输入一种语言”:否。如果该类型与运行时值(而不是变量/字段/等)相关联,则该语言是动态输入。
Paul Biggar

15
不正确的静态类型化意味着“参考值明显地(与编译时不同)受其可以表示的值类型的限制,并且语言实现(无论是编译器还是解释器) ,两者都尽可能地实施和使用这些约束。” 引用自c2.com/cgi/wiki?StaticTyping,据我所知,这是正确的。
马提亚斯·沃尔夫

6
关于Java,C,C ++,Pascal和许多其他广泛使用的“行业”语言的类型系统,最明显的地方不是它们是静态类型的,而是它们是显式类型的。换句话说,它们需要大量的类型声明。(在不太明确类型的语言中,这些声明是可选的,它们通常称为“类型注释”。)这与静态类型无关。继续..
Vipresh 2014年

7
第一种静态类型化的语言是根据需要显式地进行类型化的。但是,类型推断算法已经存在很多年了,这些技术用于查看根本没有类型声明的源代码,并确定其变量的类型。使用它的ML语言。经过改进的Haskell现在已有15年的历史了。甚至C#现在都采用了这种想法,这将引起很多人的注意,并且无疑会引起人们对其“弱类型”的说法。继续...
Vipresh 2014年

399

静态类型的编程语言在编译时(而不是运行时)进行类型检查(即,验证和加强类型约束的过程)

动态类型的编程语言在运行时(而不是在编译时)进行类型检查。

静态类型语言的示例包括:-Java,C,C ++

动态类型语言的示例包括:-Perl,Ruby,Python,PHP,JavaScript


19
我认为这是最好的答案。特别是,所接受的答案在很大程度上是事实不正确的。
乔恩·哈罗普

1
@JonHarrop具体用什么方式?
1252748 2014年

21
@thomas:“这意味着您作为程序员可以更快地编写代码,因为您不必每次都指定类型。” 如果您有类型推断,则不必每次都使用静态类型来指定类型。参见SML,OCaml,F#,Haskell ...
Jon Harrop 2014年

1
在静态类型的编程序语言中,类型检查是在运行时之前完成的,但并非完全在编译时进行。
路易斯·费利佩

307

这是一个示例,对比了Python(动态类型)和Go(静态类型)如何处理类型错误:

def silly(a):
    if a > 0:
        print 'Hi'
    else:
        print 5 + '3'

Python会在运行时进行类型检查,因此:

silly(2)

运行完美,并产生预期的输出Hi。仅当碰到有问题的行时才会引发错误:

silly(-1)

产生

TypeError: unsupported operand type(s) for +: 'int' and 'str'

因为相关行实际上已执行。

另一方面在编译时进行类型检查:

package main

import ("fmt"
)

func silly(a int) {
    if (a > 0) {
        fmt.Println("Hi")
    } else {
        fmt.Println("3" + 5)
    }
}

func main() {
    silly(2)
}

上面的代码不会编译,并出现以下错误:

invalid operation: "3" + 5 (mismatched types string and int)

5
感谢您的示例整洁。因此,我推断所有脚本语言都是动态类型的,因为它们没有被编译?
CᴴᴀZ

8
是。所有脚本语言都是动态类型的,因为它们都不是编译器来进行静态类型检查。本文sitepoint.com/typing-versus-dynamic-typing中已说明了这一点。
沙氏

9
Scala可以用作脚本语言,并且是静态类型的!#discussion @Shashi
Sagiruddin Mondal'1

3
@Shashi编译不是静态类型。runhaskell例如,可以用Haskell解释。
BalinKingOfMoria恢复CM,2013年

2
同样,脚本语言也不意味着解释语言。TypeScript是静态键入,编译/转换的,但是是脚本语言。
metalim

161

简而言之:在静态类型语言中,变量的类型是静态的,这意味着一旦将变量设置为类型,就无法对其进行更改。那是因为类型是与变量关联的,而不是变量所引用的值。

例如在Java中:

String str = "Hello";  //variable str statically typed as string
str = 5;               //would throw an error since str is supposed to be a string only

另一方面:在动态类型语言中,变量的类型是动态的,这意味着在将变量设置为类型之后,可以对其进行更改。那是因为类型是与其假定的值相关联的,而不是变量本身。

例如在Python中:

str = "Hello" # variable str is linked to a string value
str = 5       # now it is linked to an integer value; perfectly OK

因此,最好将动态类型化语言中的变量视为指向类型化值的通用指针

总而言之,类型描述(或应该描述)语言中的变量,而不是语言本身。最好将它用作具有静态类型变量的语言,而不是具有动态类型变量恕我直言的语言

静态类型的语言通常是编译语言,因此,编译器会检查类型(完全正确吗?因为以后不允许在运行时更改类型)。

动态类型语言通常被解释,因此类型检查(如果有)在使用它们时在运行时进行。当然,这带来了一些性能成本,并且是动态语言(例如python,ruby,php)的扩展性不如类型化的语言(java,c#等)的原因之一。从另一个角度来看,静态类型的语言具有更多的启动成本:使您通常编写更多的代码,更难的代码。但这会在以后得到回报。

好的是,双方都从另一方借用了功能。类型化语言包含更多动态特性,例如c#中的泛型和动态库,动态语言包含更多类型检查,例如python中的类型注释或PHP的HACK变体,它们通常不是该语言的核心,并且可以在其上使用需求。

在技​​术选择方面,任何一方都没有另一方固有的优势。您是否希望从一开始就拥有更多控制权或具有灵活性,这只是一个偏好问题。只需选择适合该工作的工具,并确保在考虑更换之前先检查相反的工具。


8
这很有道理。我认为它至少比这里的其他答案更好地解释了名称背后的原因。
JamEngulfer '16

根据此参考资料,Python既是静态类型的语言又是动态类型的语言:wiki.python.org/moin / ...有人知道为什么吗?
modulitos '16

1
卢卡斯(Lucas),相反,该文档不断重复说明Python是强类型还是动态类型。你在哪里看到的?你能报价吗?
mehmet 16'Apr 26'20

2
我认为这个答案最能以最简单的方式传达这个概念。许多其他答案试图抽象地描述该概念,但在某些细节上失败了。我希望此答案在列表顶部。
Hawkeye

5
大多数其他答案在我脑海中引发了更多问题。这清除了所有人。这个答案的确应该在恕我直言的顶部
哈密​​托伦

39

http://en.wikipedia.org/wiki/Type_system

静态打字

当在编译时(而不是运行时)执行类型检查时,一种编程语言被称为使用静态类型。在静态类型化中,类型与变量而不是值相关联。静态类型的语言包括Ada,C,C ++,C#,JADE,Java,Fortran,Haskell,ML,Pascal,Perl(关于区分标量,数组,哈希和子例程)和Scala。静态类型化是程序验证的一种有限形式(请参见类型安全性):因此,它允许在开发周期的早期捕获许多类型错误。静态类型检查器仅评估可在编译时确定的类型信息,但能够验证所检查的条件是否适用于程序的所有可能执行,这样就不必在每次执行程序时都重复进行类型检查。通过省略运行时类型检查并启用其他优化,也可以使程序执行更有效(即更快或减少内存)。

由于它们在编译期间评估类型信息,因此缺少仅在运行时可用的类型信息,因此静态类型检查器是保守的。他们将拒绝某些程序,这些程序在运行时可能表现良好,但无法静态确定其类型正​​确。例如,即使表达式在运行时始终求值为true,包含该代码的程序

if <complex test> then 42 else <type error>

将被拒绝为错误类型,因为静态分析无法确定不会采用else分支。[1] 静态类型检查器的保守行为在不经常为假的情况下很有用:静态类型检查器可以检测很少使用的代码路径中的类型错误。如果没有静态类型检查,那么即使代码覆盖率测试达到100%,也可能无法找到此类类型错误。代码覆盖率测试可能无法检测到此类类型错误,因为必须考虑创建值的所有位置和使用某个值的所有位置的组合。

最广泛使用的静态类型语言不是形式安全的。它们在编程语言规范中具有“漏洞”,使程序员可以编写代码来规避由静态类型检查器执行的验证,从而解决更广泛的问题。例如,Java和大多数C风格的语言都具有punning类型,而Haskell具有unsafePerformIO之类的功能:这样的操作在运行时可能是不安全的,因为它们可能在程序运行时由于错误地键入值而导致不良行为。

动态打字

当一种编程语言的大部分类型检查在运行时而不是在编译时执行时,就被称为动态类型的,或者只是“动态”的。在动态类型化中,类型与值而不是变量相关联。动态类型语言包括Groovy,JavaScript,Lisp,Lua,Objective-C,Perl(关于用户定义类型,但不是内置类型),PHP,Prolog,Python,Ruby,Smalltalk和Tcl。与静态类型相比,动态类型可以更灵活(例如,通过允许程序基于运行时数据生成类型和功能),尽管以牺牲先验保证为代价。这是因为动态类型化的语言接受并尝试执行某些程序,这些程序可能会被静态类型检查器判定为无效。

动态类型可能会导致运行时类型错误-也就是说,在运行时,值可能具有意外的类型,并且对该类型进行了无意义的操作。此操作可能会在发生编程错误的位置(即,错误的数据类型传递到不应有的位置)之后发生。这使错误很难找到。

与它们的静态类型表亲相比,动态类型语言系统对源代码进行的“编译时”检查更少(但例如,它将检查程序在语法上是否正确)。运行时检查可能会更复杂,因为它们可以使用动态信息以及编译期间存在的任何信息。另一方面,运行时检查仅断言条件在特定的程序执行中成立,并且对于程序的每次执行重复这些检查。

动态类型语言的开发通常受到诸如单元测试之类的编程实践的支持。测试是专业软件开发中的关键实践,在动态类型的语言中尤其重要。实际上,为确保正确的程序操作而进行的测试可以检测到比静态类型检查更大范围的错误,但是相反,不能像测试和静态类型检查一样全面地搜索错误。测试可以合并到软件构建周期中,在这种情况下,可以将其视为“编译时”检查,因为程序用户将不必手动运行此类测试。

参考文献

  1. 皮尔斯·本杰明(2002)。类型和编程语言。麻省理工学院出版社。ISBN 0-262-16209-1。

75
SO的主要思想是建立知识体系,而不是提供与其他地方的链接。您应该至少尝试摘录回答问题的Wiki的摘录。
NomeN

5
由于它是到Wikipedia的链接,而不是某些瞬态网站的链接,因此似乎只是多余的,但是下次我会记得的。
雅各布

2
我仍然无法以某种方式想到动态类型语言中的示例,该类型在编译时不清楚,但必须在运行时确定。你能给我一些吗?
Novellizator 2014年

3
@Novellizator旧评论,但想象一个场景,其中从远程服务器中拾取一些数据,然后使用这些数据来选择对象的属性。例如:myObject[remoteDataName]。这样就无法知道它将选择哪个属性,甚至根本不是一个有效的属性。
Mike Cluck 2014年

14

不幸的是,术语“动态类型化”具有误导性。所有语言都是静态类型的,类型是表达式的属性(不是某些人认为的值)。但是,某些语言只有一种类型。这些被称为单型语言。这种语言的一个例子是未类型化的lambda演算。

在未类型化的lambda演算中,所有项都是lambda项,并且可以对一项执行的唯一操作是将其应用于另一项。因此,所有操作总是导致无限递归或lambda项,但从不表示错误。

但是,如果我们用原始数字和算术运算来扩充未类型化的lambda演算,那么我们可以执行无意义的运算,例如将两个lambda项加在一起:(λx.x) + (λy.y)。有人可能会说,唯一明智的做法是在发生这种情况时发出错误信号,但要做到这一点,每个值都必须用指示该术语是lambda术语还是数字的指示符进行标记。然后,加法运算符将检查两个参数是否确实标记为数字,如果不是,则表明错误。请注意,这些标记不是类型,因为类型是程序的属性,而不是那些程序产生的值。

执行此操作的单类型语言称为动态类型。

JavaScript,Python和Ruby之类的语言都是单类型的。同样,typeofJavaScript中的运算符和typePython中的函数具有误导性的名称。它们返回与操作数关联的标签,而不是它们的类型。同样,dynamic_cast在C ++和instanceofJava中都没有做类型检查。


7

编译与解释

“翻译源代码时”

  • 源代码:原始代码(通常由人类输入到计算机中)
  • 翻译:将源代码转换为计算机可以读取的内容(即机器代码)
  • 运行:程序执行命令的时间段(编译后,如果已编译)
  • 编译语言:运行前翻译的代码
  • 口译语言:执行期间即时翻译的代码

打字

“检查类型时”

5 + '3'强类型语言(例如Go和Python)中类型错误的一个示例,因为它们不允许“类型强制”->在某些情况下(例如合并两个类型),值可以更改类型。诸如JavaScript之类的弱类型语言不会引发类型错误(导致'53')。

  • 静态:运行时检查的类型
  • 动态:执行期间即时检查类型

“静态和编译”和“动态和解释”的定义非常相似...但是请记住,这是“何时检查类型”与“何时翻译源代码”。

无论语言是编译还是解释,您都会遇到相同的类型错误!您需要在概念上分开这些术语。


Python示例

动态,口译

def silly(a):
    if a > 0:
        print 'Hi'
    else:
        print 5 + '3'

silly(2)

因为Python既是解释型的又是动态类型的,所以它只翻译和类型检查在其上执行的代码。该else块永远不会执行,因此5 + '3'甚至不会被查看!

如果它是静态类型的怎么办?

代码甚至在运行之前都会引发类型错误。即使它被解释,它仍然在运行时之前执行类型检查。

如果已编译怎么办?

else块将在运行时之前被翻译/查看,但是由于它是动态键入的,因此不会引发错误!动态类型语言在执行之前不会检查类型,并且该行永远不会执行。


去例子

静态,已编译

package main

import ("fmt"
)

func silly(a int) {
  if (a > 0) {
      fmt.Println("Hi")
  } else {
      fmt.Println("3" + 5)
  }
}

func main() {
  silly(2)
}

在运行(静态)之前检查类型,并立即捕获类型错误!如果对类型进行了解释,则在运行时仍将对其进行检查,结果相同。如果它是动态的,则即使在编译过程中查看代码,也不会引发任何错误。


性能

如果是静态类型(与动态类型相比),则编译后的语言在运行时将具有更好的性能。类型知识允许机器代码优化。

静态类型的语言本质上在运行时具有更好的性能,因为不需要在执行时动态检查类型(它在运行之前进行检查)。

同样,编译后的语言在运行时速度更快,因为代码已被翻译,而不需要即时“解释” /翻译代码。

请注意,编译语言和静态类型语言在运行翻译和类型检查之前都会分别有一个延迟。


更多差异

静态类型可以及早发现错误,而不是在执行过程中发现错误(这对长程序尤其有用)。它更具“严格性”,因为它不允许在程序的任何位置出现类型错误,并且通常防止变量更改类型,从而进一步防止了意外错误。

num = 2
num = '3' // ERROR

动态键入更加灵活,有些人对此表示赞赏。它通常允许变量更改类型,这可能导致意外错误。


因为Python既是解释型的又是动态类型的,所以它仅转换和类型检查正在其上执行的代码 -事实并非如此。Python(至少是参考实现)在导入时会编译所有代码(您也可以在导入之前/不导入模块之前进行编译)。编译器引入了各种优化(至少在Python的动态特性允许的范围内)。
Eli Korvigo

6

静态类型语言:每个变量和表达式在编译时都是已知的。

int a;a在运行时只能采用整数类型值)

示例:C,C ++,Java

动态类型语言:变量在运行时可以接收不同的值,并且它们的类型在运行时定义。

var a;a在运行时可以采用任何类型的值)

示例:Ruby,Python。


5

静态类型语言在编译时进行类型检查,并且类型不能更改。(不要在类型转换注释中使用,创建新变量/引用)。

动态类型语言在运行时进行类型检查,并且可以在运行时更改变量的类型。


4

简单,甜美的定义,但满足需要:静态类型化的语言将类型绑定到变量的整个范围(例如:SCALA);动态类型化的语言将类型绑定到变量引用的实际值。


3
  • 在静态类型语言中,变量与在编译时已知的类型相关联,并且在程序执行期间该类型保持不变。等效地,只能为变量分配一个值,该值是已知/指定类型的实例。
  • 在动态类型语言中,变量没有类型,执行期间其值可以是任何形状和形式的任何值。

2

静态类型的语言(如C ++,Java)和动态类型的语言(如Python)仅在变量类型执行方面有所不同。 静态类型的语言具有用于变量的静态数据类型,此处的数据类型在编译过程中进行检查,因此调试要简单得多...而动态类型的语言则不执行此操作,因此会检查执行程序的数据类型,从而检查调试有点困难。

此外,它们之间的差异很小,可以与强类型弱类型语言相关。强类型语言不允许您将一种类型用作另一种类型。C和C ++ ...而弱类型语言则允许使用例如python


2

静态类型

在运行时检查类型,以便更早发现错误。

例子= c ++

动态类型

在执行期间检查类型。

示例= Python


2
这并没有真正添加其他答案尚未涵盖的内容,不是吗?
罗伯特

1
是的,但是大多数答案不是很清楚,所以我想要一个易于理解的答案。
Atticus Denewmont

1

静态类型语言(编译器解析方法调用并编译引用):

  • 通常表现更好
  • 更快的编译错误反馈
  • 更好的IDE支持
  • 不适合使用未定义的数据格式
  • 未定义模型时更难以开始开发
  • 编译时间更长
  • 在许多情况下,需要编写更多代码

动态类型语言(正在运行的程序中做出的决定):

  • 较低的表现
  • 更快的发展
  • 可能仅在运行时稍后才检测到某些错误
  • 适用于未定义的数据格式(元编程)


-15

静态键入: Java和Scala等语言是静态类型的。

在代码中使用变量之前,必须先对其进行定义和初始化。

对于前。int x; x = 10;

System.out.println(x);

动态打字: Perl是一种动态打字语言。

在代码中使用变量之前,无需对其进行初始化。

y = 10;在代码的后面部分使用此变量


2
好吧,这个答案并不完全正确。在这两种语言中,必须先初始化变量,然后再使用它们。但是,在动态类型的语言中,您可以选择省略使用它的类型。
darshan 2014年

您似乎误用了“变量”一词,应该改用“类型”。
埃米尔(Emir)2014年

我认为Perl是静态类型的:它具有3种类型,标量($),数组(@)和哈希(%)。Perl中的变量类型在编译时是已知的,并且在变量生存期的其余时间内保持不变。
CoffeeTableEspresso
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.