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

Rac*_*hel 894 computer-science programming-languages static-typing dynamic-typing

我听到很多新的编程语言都是动态输入的,但是当我们说一种语言是动态类型而非静态类型时,它实际意味着什么呢?

Nom*_*meN 810

静态类型语言

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

这里的主要优点是所有类型的检查都可以由编译器完成,因此很早就会发现许多琐碎的错误.

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

动态类型语言

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

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

大多数脚本语言都具有此功能,因为无论如何都没有编译器进行静态类型检查,但您可能会发现自己正在搜索由于解释器误解变量类型而导致的错误.幸运的是,脚本往往很小,所以bug没有那么多地方可以隐藏.

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

  • "如果在运行时解释变量的类型,则动态类型化语言":否.如果类型与运行时值相关联,则动态类型化语言,而不是命名变量/字段/等. (85认同)
  • 不正确的静态类型意味着"显而易见的参考值(与编译时不同)受限于它可以表示的值的类型,以及语言实现,无论是编译器还是解释器,都尽可能地强制执行和使用这些约束." 引自:http://c2.com/cgi/wiki ?StaticTyping,我理解它是正确的. (14认同)
  • 第一种静态类型语言是必要时明确键入的.但是,类型推断算法 - 用于查看完全没有类型声明的源代码的技术,以及决定其变量的类型现在已经存在很多年了.ML语言,使用它.改进它的Haskell现在已经有15年的历史了.即使是C#现在也采用了这个想法,这会引起很多人的关注,并且无疑会引起人们对其"虚弱打字"的说法.继续... (7认同)
  • @NomeN你能说出任何实现HM类型推断的动态类型语言吗? (6认同)
  • 关于Java,C,C++,Pascal和许多其他广泛使用的"行业"语言的类型系统最明显的不是它们是静态类型的,而是它们是明确键入的.换句话说,它们需要大量的类型声明.(在不太明确的类型语言的世界中,这些声明是可选的,它们通常被称为"类型注释".)这与静态类型无关.继续.. (6认同)
  • @NomeN -1"这在实践中意味着你作为程序员必须指定每个变量的类型......这意味着你作为程序员可以更快地编写,因为你不必每次都指定类型".C#有`var`,C++有'auto`,所有来自ML家族的语言(20世纪70年代后期,例如Standard ML,OCaml,Haskell,F#)都有完整的Hindley-Milner类型推断,所以你不注释*any*类型.所以你的陈述完全是错误的. (3认同)
  • "HM型推断"是"Hindley-Milner型推断"; 请参阅http://en.wikipedia.org/wiki/Type_inference动态类型语言通常不称为类型推断. (2认同)
  • 如果不喜欢类型声明,最好将其准确描述为不喜欢显式类型,而不是静态类型。来源http://cdsmith.wordpress.com/2011/01/09/an-old-article-i-wrote/ (2认同)
  • 你写了“...解释器误解了变量的类型。” 但你的意思一定是“......编码器误解了变量的类型。” :) (2认同)

Chr*_*kar 368

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

动态类型编程语言在运行时进行类型检查,而不是编译时.

  • @thomas:"这意味着你作为程序员可以更快地编写,因为你不必每次都指定类型".如果您有类型推断,则不必每次都使用静态类型指定类型.参见SML,OCaml,F#,Haskell ...... (20认同)
  • 我认为这是最好的答案.特别是,接受的答案在很大程度上是不正确的. (16认同)
  • 在静态类型程序语言中,类型检查是在运行前完成的,但并不完全是在编译时完成。 (3认同)
  • @jumping_monkey我认为你混淆了*动态类型*和*类型推断*。使用“var”,您可以告诉编译器,“请为我推断出类型”。然后,该程序将像您编写该特定类型一样进行编译。当变量使用不正确时,编译器会发出类型错误。正如 NomeN 的回答所指出的那样,动态类型意味着该类型仅在运行时才知道(并且实际上与变量无关)。Java当然完全不是这样的。 (3认同)
  • @JonHarrop 具体以什么方式? (2认同)

Aka*_*all 295

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

def silly(a):
    if a > 0:
        print 'Hi'
    else:
        print 5 + '3'
Run Code Online (Sandbox Code Playgroud)

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

silly(2)
Run Code Online (Sandbox Code Playgroud)

运行完美,并产生预期的输出Hi.只有在遇到有问题的行时才会引发错误:

silly(-1)
Run Code Online (Sandbox Code Playgroud)

产生

TypeError: unsupported operand type(s) for +: 'int' and 'str'
Run Code Online (Sandbox Code Playgroud)

因为相关的线路实际上已经执行了.

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

package main

import ("fmt"
)

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

func main() {
    silly(2)
}
Run Code Online (Sandbox Code Playgroud)

以上将无法编译,出现以下错误:

invalid operation: "3" + 5 (mismatched types string and int)
Run Code Online (Sandbox Code Playgroud)

  • Scala可以用作脚本语言,它是静态类型的!#discussion @Shashi (9认同)
  • 是.所有脚本语言都是动态类型的,因为无论如何它们都不是编译器来进行静态类型检查.这一点已在本文http://www.sitepoint.com/typing-versus-dynamic-typing/中说明. (7认同)
  • 谢谢你整洁的例子.所以我推断所有脚本语言都是动态类型的,因为它们没有编译? (5认同)
  • @Shashi编译并不意味着静态类型.例如,Haskell可以用`runhaskell`来解释. (3认同)
  • 同样,脚本语言也不意味着解释语言。TypeScript是静态键入,编译/转换的,但是是脚本语言。 (2认同)
  • “一段代码”值一千字。之前答案中的所有冗长都没有像这段代码那样点亮我脑海中的灯泡。现在,这种冗长是绝对完美的:) 非常感谢。 (2认同)
  • 这应该被接受为最佳答案。并不是说目前接受的观点是错误的,但读到这篇文章我有一个“啊哈!” 片刻。 (2认同)
  • @CᴴᴀZ 没那么简单。Python 不是一种纯解释性语言,它运行一个优化编译器来生成解释性字节码。正如许多人已经提到的,被解释与动态类型没有什么关系。这是一个设计选择。静态语言提供的灵活性要低得多,但反过来却提供了增强的优化和错误跟踪能力。然而,众所周知,有些事情很难用静态类型来表达(例如函数式编程:Haskell 和 Scala 利用令人难以置信的复杂类型系统来使静态 FP 具有表现力和效率)。 (2认同)
  • @CᴴᴀZ:编译和解释是编译器或解释器的特征(废话!)而不是语言的特征。每种语言都可以由解释器实现,每种语言都可以由编译器实现。我认为您对这个问题的几乎所有答案都声称“静态类型”意味着“编译时类型检查”而“动态类型”意味着“运行时类型检查”这一事实感到困惑。那是错的。要100%正确,它应该是“静态类型”,意思是“*运行时*之前进行类型检查”。Gofer 是由解释器实现的 Haskell 方言(现已不复存在)。…… (2认同)

meh*_*met 153

简单地说就是这样:在静态类型语言中,变量的类型是静态的,这意味着一旦将变量设置为类型,就无法更改它.这是因为键入与变量而不是它所引用的值相关联.

例如在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
Run Code Online (Sandbox Code Playgroud)

另一方面:在动态类型语言中,变量的类型是动态的,这意味着在将变量设置为类型后,您可以更改它.这是因为打字与它所假定的值相关联,而不是与变量本身相关联.

例如在Python中:

str = "Hello" # variable str is linked to a string value
str = 5       # now it is linked to an integer value; perfectly OK
Run Code Online (Sandbox Code Playgroud)

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

总而言之,类型描述(或应该描述)语言中的变量而不是语言本身.它可以更好地用作具有静态类型变量的语言,而不是具有动态类型变量 IMHO 的语言.

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

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

好处是双方都在借用另一方的特征.类型化语言包含更多动态特性,例如c#中的泛型和动态库,动态语言包括更多类型检查,例如python中的类型注释,或PHP的HACK变体,它们通常不是语言的核心,可用于需求.

在技​​术选择方面,任何一方都没有内在优势.您是想要更多控制开始还是灵活性,这只是一个偏好问题.只需为工作挑选合适的工具,并确保在考虑开关之前检查相反的可用工具.

  • 这很有道理.我认为至少可以解释这些名字背后的原因比其他答案要好得多. (8认同)
  • 大多数其他答案在我的脑海中创造了更多问题 这个清除了所有这些.这个答案真的应该在顶级恕我直言 (4认同)
  • 我认为这个答案最能以最简单的方式传达这个概念.许多其他答案试图抽象地描述概念,但在某些细节上失败.我宁愿在列表的顶部看到这个答案. (2认同)
  • 我认为“打字”这个词妨碍了清晰度。你的回答让我明白了:) 过去每次我听到这个,我都在考虑在键盘上打字,就像与声明或不声明变量的方式有关;我从来没有想过数据类型。因此,“类型化”是指变量的数据类型改变状态的能力,无论变量的数据类型是静态的还是动态的。前任。动态(Str -> int -> Boolean) (2认同)

Jac*_*cob 38

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>
Run Code Online (Sandbox Code Playgroud)

因为静态分析无法确定不会采取其他分支,因此将被拒绝为错误类型.[1] 当不经常评估错误时,静态类型检查器的保守行为是有利的:静态类型检查器可以检测很少使用的代码路径中的类型错误.如果没有静态类型检查,即使具有100%代码覆盖率的代码覆盖率测试也可能无法找到此类型错误.代码覆盖率测试可能无法检测到此类型错误,因为必须考虑创建值的所有位置和使用特定值的所有位置的组合.

最广泛使用的静态类型语言不是正式类型安全的.它们在编程语言规范中存在"漏洞",使程序员能够编写绕过静态类型检查器执行的验证的代码,从而解决更广泛的问题.例如,Java和大多数C风格的语言都有类型惩罚,而Haskell具有unsafePerformIO等功能:这样的操作在运行时可能不安全,因为它们会在程序运行时由于错误输入值而导致不必要的行为.

动态打字

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

动态类型可能会导致运行时类型错误 - 也就是说,在运行时,值可能具有意外类型,并且应用了对该类型无意义的操作.这种操作可能在编程错误发生的地方很久之后发生 - 也就是说,错误类型的数据传递到它不应该具有的地方的地方.这使得很难找到bug.

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

动态类型语言的开发通常由单元测试等编程实践支持.测试是专业软件开发中的关键实践,在动态类型语言中尤为重要.在实践中,为确保正确的程序操作而进行的测试可以检测比静态类型检查更广泛的错误,但相反地,不能全面地搜索测试和静态类型检查能够检测到的错误.测试可以合并到软件构建周期中,在这种情况下,它可以被认为是"编译时"检查,因为程序用户不必手动运行这样的测试.

参考

  1. 皮尔斯,本杰明(2002年).类型和编程语言.MIT出版社.国际标准书号0-262-16209-1.

  • SO的主要思想是建立一个知识体系,而不是提供与其他地方的链接.您应该尝试至少制作一个能够回答问题的wiki的摘录. (75认同)
  • 它似乎是多余的,因为它是维基百科的链接,而不是一些短暂的网站,但我会记得下次. (5认同)
  • @Novellizator旧评论但想象一下这样一种情况:从远程服务器获取一些数据然后该数据用于从对象中挑选一个属性.例如:`myObject [remoteDataName]`.然后就无法知道它将选择哪个属性,甚至根本不知道它是一个有效的属性. (3认同)
  • 无论如何,我已根据大众需求更新了答案. (2认同)
  • 不知怎的,我仍然无法想到动态类型语言中的一个例子,其中类型在编译时不清楚但必须在运行时计算出来.你能帮我个忙吗? (2认同)

rig*_*old 14

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

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

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

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

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

  • 想象一下,回答这个问题的答案会让我们再问十个问题...... (4认同)

deb*_*yan 13

在编程中,数据类型是一种分类,它告诉我们 1) 变量将保存什么类型的值,2) 可以对这些值执行哪些数学、关系和逻辑运算而不会出错。

当人们在以下上下文中提到“类型”时,他们的意思是“数据类型”。

在每种编程语言中,为了最大限度地减少出现错误的机会,都会在程序执行之前期间进行类型检查。根据类型检查的时机,有两种类型的编程语言:静态类型动态类型语言(或混合语言,用户指定每个变量使用哪种(静态/动态))。

此外,根据是否发生隐式类型转换,有两种类型的编程语言:强类型语言和弱类型语言。

打字-静态与动态,强与弱

静态类型:

  • 类型检查在编译时完成

  • 在源代码中,在变量声明时(将值分配给新变量时),必须显式指定该变量的数据类型(即不能是隐式/推断/猜测的),因为如果数据类型在源代码中指定,然后在编译时源代码将转换为机器代码并允许进行类型检查

  • 这里,数据类型与变量(变量名,而不是值)相关联,例如,int count。该关联是静态的(固定的)

  • 如果我们尝试通过向已声明的变量 ( int count) 分配不同数据类型 ( ) 的值来更改int count = "Hello"其数据类型,则会收到错误

  • 如果我们尝试通过int count使用不同的数据类型 ( ) 重新声明已声明的变量 ( boolean count) 来更改数据类型,那么我们也会收到错误

int count;         /* count is int type, association between data type
                      and variable is static or fixed */

count = 10;        // no error 
count = 'Hello';   // error 
boolean count;     // error 
Run Code Online (Sandbox Code Playgroud)
  • 由于类型检查和类型错误检测是在编译时完成的,因此在运行时不需要进一步的类型检查。因此,程序变得更加优化,执行速度更快

  • 如果我们想要更多类型严格的代码,那么选择这种类型的语言是更好的选择

  • 示例:Java、C、C++、Go、Swift 等。

动态类型:

  • 类型检查在运行时完成

  • 在源代码中,在声明变量时,不需要显式指定该变量的数据类型。由于类型检查是在运行时完成的,因此语言系统根据该变量分配值的数据类型确定变量类型

  • 这里,数据类型与分配给变量的值相关联。例如,var foo = 1010 是一个数字,所以现在 foo 是数字数据类型。但这种关联是动态的(灵活的)

  • 我们可以var foo = 10通过将不同数据类型()的值分配foo = "Hi"给已声明的变量()来轻松更改其数据类型,而不会产生错误

  • 我们可以轻松地更改已声明变量 ( var foo = 10) 的数据类型,方法是使用其他数据类型 ( ) 的值重新声明它var foo = true,而不会产生错误

var foo;            // without assigned value, variable holds undefined data type 

var foo = 10;       // foo is Number type now, association between data 
                    // type and value is dynamic / flexible 
foo = 'Hi';         // foo is String type now, no error 
var foo = true;     // foo is Boolean type now, no error 
Run Code Online (Sandbox Code Playgroud)
  • 由于类型检查和类型错误检测是在运行时完成的,动态类型程序的优化程度较低,导致执行速度较慢。尽管这些类型的语言如果实现 JIT(即时)编译器,执行速度会更快

  • 如果我们想轻松地编写和执行代码,那么这种类型的语言是更好的选择,但在这里我们仍然会不幸地得到运行时错误

  • 示例:Python、JavaScript、PHP、Ruby 等。


JBa*_*lin 7

编译与解释

"当源代码被翻译时"

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

打字

"检查类型时"

5 + '3'强类型语言(如Go和Python)中类型错误的一个示例,因为它们不允许"类型强制" - >值在某些上下文中更改类型的能力,例如合并两种类型.弱类型语言(如JavaScript)不会引发类型错误(导致'53').

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

"静态与编译"和"动态与解释"的定义非常相似......但请记住"当检查类型时"与"源代码翻译时".

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


Python示例

动态,解释

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

silly(2)
Run Code Online (Sandbox Code Playgroud)

因为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)
}
Run Code Online (Sandbox Code Playgroud)

在运行之前检查类型(静态)并立即捕获类型错误!如果解释了类型,则在运行时仍会检查这些类型,结果相同.如果它是动态的,即使在编译期间查看代码,它也不会抛出任何错误.


性能

如果编译语言是静态类型(动态对比),则它在运行时会有更好的性能; 类型知识允许机器代码优化.

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

类似地,编译语言在运行时更快,因为代码已经被翻译而不是需要动态地"解释"/翻译它.

请注意,编译和静态类型语言在运行转换和类型检查之前都会有延迟.


更多的差异

静态类型提前捕获错误,而不是在执行期间找到它们(对于长程序尤其有用).它更加"严格",因为它不会在程序中的任何地方出现类型错误,并且通常会阻止变量更改类型,从而进一步防止意外错误.

num = 2
num = '3' // ERROR
Run Code Online (Sandbox Code Playgroud)

动态类型更灵活,有些人欣赏.它通常允许变量更改类型,这可能导致意外错误.


Ram*_*pta 6

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

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

示例:C,C ++,Java

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

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

示例:Ruby,Python。


RES*_*rey 5

静态类型语言在编译时进行类型检查,类型不能更改.(对于类型转换注释不要太可爱,创建一个新的变量/引用).

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


小智 5

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