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

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

答案

静态类型语言

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

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

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

动态类型语言

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

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

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

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

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

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

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

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

def silly(a):
    if a > 0:
        print 'Hi'
    else:
        print 5 + '3'
silly(2)
silly(-1)
package main

import ("fmt"
)

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

func main() {
    silly(2)
}
String str = "Hello";  //variable str statically typed as string
str = 5;               //would throw an error since str is supposed to be a string only
str = "Hello" # variable str is linked to a string value
str = 5       # now it is linked to an integer value; perfectly OK

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] 静态类型检查器的保守行为在不经常评估为 false 时是有利的:静态类型检查器可以检测很少使用的代码路径中的类型错误。如果没有静态类型检查,那么即使代码覆盖率测试达到 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。

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

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

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

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

JavaScript,Python 和 Ruby 之类的语言都是单类型的。同样,JavaScript 中的typeof运算符和 Python 中的type函数的名称具有误导性。它们返回与操作数关联的标签,而不是它们的类型。同样,C ++ 中的dynamic_cast和 Java 中的instanceof 也不进行类型检查。

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

silly(2)
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

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

int a; a 在运行时只能接受整数类型值)

示例:C,C ++,Java

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

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

示例:Ruby,Python。

静态类型语言在编译时进行类型检查,并且类型不能更改。 (不要对类型转换注释产生兴趣,会创建一个新的变量 / 引用)。

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

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