静态/动态 vs 强类型/弱类型

376

静态类型与动态类型以及强类型与弱类型有什么区别?


11个回答

506
  • 静态/动态类型 是关于 类型信息何时获取的(是在编译时还是在运行时获取)。

  • 强类型/弱类型 是关于 类型严格程度如何 区分的(例如,语言是否尝试将字符串隐式转换为数字)。

请参阅维基页面以获取更详细的信息。


10
维基百科拥有所有答案。为什么我之前没有偶然发现它呢,我不知道。 - Daniel Revell
37
很遗憾许多人不知道静态/动态和强类型/弱类型是两回事... 如果能明白这个,将会减少很多偏见和争议。 - Dykam
10
“类型弱化”的程度是不同的。一个强类型语言可能会尝试将字符串转换为数字。另一方面,HyperTalk(我几十年前使用过的一种语言)是如此弱类型化,以至于"12" + "34"将等于"46",但"12" + "34Q"将等于"1234Q" [幸运的是,如果想要连接字符串,可以写成"12"&"34"]。有趣的是,存储数字的变量将其存储为双精度浮点数,并且对这些变量进行数学计算时使用浮点值而不进行字符串操作,但是没有办法询问变量是字符串还是数字。 - supercat
9
@kittylyst这个答案并没有暗示strong是static的同义词。 - Pete
4
++用于(大致的)单行定义。 - JamesFaix
显示剩余3条评论

233
您发现了业余程序员用来谈论编程语言术语的一个漏洞。 不要使用“strong”和“weak”类型这些术语,因为它们没有普遍认可的技术含义。相比之下,静态类型意味着程序在执行之前被检查,可能会在开始之前被拒绝。而动态类型意味着在执行过程中检查的类型,一个类型不匹配的操作可能会导致程序停止或者在运行时发出错误信号。静态类型的主要原因是排除可能存在“动态类型错误”的程序。 强类型通常表示类型系统中没有漏洞,而弱类型则表示类型系统可能被破坏(从而使任何保证失效)。这些术语经常被错误地用来表示静态和动态类型。 要看到区别,请想想C:该语言在编译时进行类型检查(静态类型),但存在许多漏洞;您可以将任何类型的值几乎转换为大小相同的其他类型——特别是,可以自由地转换指针类型。Pascal是一个旨在实现强类型的语言,但却有一个著名的漏洞:没有标记的变体记录。
强类型语言的实现通常随着时间的推移而获得漏洞,通常是为了能够在高级语言中实现其中一部分运行时系统。例如,Objective Caml有一个名为Obj.magic的函数,其运行时效果只是返回其参数,但在编译时它将任何类型的值转换为另一种类型。我最喜欢的例子是Modula-3,其设计者称其类型转换结构为LOOPHOLE
话虽如此,您不能保证任何两个人都以完全相同的方式使用“strong”和“weak”这些单词。因此要避免使用它们。

35
请避免使用“强”和“弱”这些词汇,以下是您的建议: - Nico
1
同意,我刚刚在阅读Jon Skeet的书,这是书中提到的相同回应。 - Bennett Keller
据我所知,Java 也存在这些漏洞,但它仍被认为是一种强类型语言,因此我想这更加支持你避免使用“强”和“弱”这些术语的建议。 - doubleOrt

99

简单来说,静态类型语言的类型是静态的,这意味着一旦将变量设置为某种类型,就无法更改。这是因为类型与变量相关联,而不是与其引用的值相关联。

例如,在Java中:

String str = "Hello";  //statically typed as string
str = 5;               //would throw an error since java is statically typed

相比之下,在动态类型语言中,类型是动态的,也就是说,你可以在将变量定义为某个类型之后再更改它。这是因为类型是与值而非变量相关联的。

例如,在Python中:

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

另一方面,语言中的强/弱类型与隐式类型转换有关(部分摘自@Dario的回答):

例如,在Python中:

str = 5 + "hello" 
# would throw an error since it does not want to cast one type to the other implicitly. 

然而在PHP中:

$str = 5 + "hello"; // equals 5 because "hello" is implicitly casted to 0 
// PHP is weakly typed, thus is a very forgiving language.

静态类型允许在编译时检查类型的正确性。通常,静态类型语言是编译型的,而动态类型语言是解释型的。因此,动态类型语言可以在运行时检查类型。


3
非常好的回答,使用具体例子值得赞赏。 - Julian A.
3
这就是为什么需要非常小心地使用PHP的原因。 - Ali Gajani
1
这些语言示例非常有帮助。非常感谢。 - jrmullen
1
从这个意义上讲,Java会稍微弱类型一些,因为您可以将非字符串与字符串连接,并且由于自动拆装箱的存在。 - Stephen Paul
1
@StephenPaul,你说得对,我的回答可能会被理解成那样,但事实并非如此。我之所以使用连接,是为了简单起见,但实际上,强度/弱度是关于变量本身的隐式类型转换。 - mehmet
显示剩余2条评论

23

弱类型意味着一个对象的类型可以根据上下文变化。例如,在弱类型语言中,如果您将数字添加到字符串“123”中,则该字符串可能被视为数字123。具有弱类型的语言包括bash、awk和PHP。

另一种弱类型语言是C语言,其中内存地址处的数据可以通过转换被视为不同的数据类型。

在强类型语言中,对象的类型不会改变-整数始终是整数,试图将其用作字符串将导致错误。Java和Python都是强类型语言。

动态类型和静态类型的区别在于何时执行类型检查。在静态类型语言中,每个变量和参数的类型必须在源代码中声明,并在编译时执行强制检查。在动态类型语言中,类型仅在运行时使用时才进行检查。因此,Java是静态类型的,而Python是动态类型的。

但有时边界可能有些模糊。例如,尽管Java是静态类型的,但每次使用反射或强制转换(例如在使用对象容器时)时,都会将类型检查推迟到运行时。

同样,大多数强类型语言仍会自动转换整数和浮点数之间的值(在某些语言中还支持任意精度的BigInts)。


1
我不能同意这个句子 - “在静态类型语言中,每个变量和参数的类型必须在源代码中声明” - 在SML中,变量的类型不需要被声明(但是它们会被检查)。假设函数f接受参数x(fun f(x)) [所以没有声明类型],函数体是x+1。没有声明类型,编译器会推断出x必须是一个整数。 - fun f x = x + 1; val f = fn : int -> int - Filip Bartuzi
关于 C 语言,强制类型转换并不违背强类型的原则。但是在 C 中也允许直接使用不同类型进行运算,无需类型转换,例如:5 + 'c' // 正确 - mehmet
4
在C语言中,字符值属于整数域,因此该特定示例不会违反类型安全性。 'c'只是99的语法糖。C没有专门的字符类型。 - Peter Lewerin
Peter Lewerin:对不起,我应该给出一个更好的例子。不幸的是,我已经有将近20年没有碰过C语言了 :) - mehmet
1
C语言并不是一种弱类型语言。相比之下,Java、C#等语言更加强类型化。如果您查看“弱类型”语言的定义,则“弱类型”语言是指可以进行任何类型的转换的语言,例如int可以被“隐式”转换或转型为字符串,现在请自行思考这是否在C中可能?详情请参阅https://en.wikipedia.org/wiki/Strong_and_weak_typing。 - hagrawal7777
@Filip Bartuzi 这类似于 C# 中的类型推断。通过使用字面量而不是显式地提到类型,程序员隐含地提供了类型。 - Tarik

18
今天在研究这个主题时,我发现了这篇很棒的文章 http://blogs.perl.org/users/ovid/2010/08/what-to-know-before-debating-type-systems.html。它为我澄清了很多事情,我认为它可能会补充到上面一些很棒的答案中。
强类型和弱类型:
最常见的类型系统分类方式可能是"强类型"或"弱类型"。这非常不幸,因为这些词几乎没有任何意义。在有限的程度上,可以比较两种具有非常相似类型系统的语言,并将其中一个指定为具有更强的系统。除此之外,这些词就毫无意义了。
静态类型和动态类型:
这几乎是唯一常见的具有真正意义的类型系统分类方式。事实上,它的重要性经常被低估[...] 动态类型和静态类型是两个完全不同的东西,其目标偶尔发生部分重叠。
静态类型系统是一种机制,通过它编译器检查源代码并给语法片段分配标签(称为“类型”),然后使用它们推断程序的行为。动态类型系统是一种机制,通过它编译器生成代码来跟踪程序使用的数据类型(巧合的是,也称为其“类型”)。当然,在这两个系统中都使用相同的“类型”词并不是完全巧合的; 然而,最好将其理解为具有一种弱历史意义。试图在这两个系统中找到“类型”实际上意味着相同的世界观会导致极大的混乱。它们并不相同。
显式/隐式类型:
当使用这些术语时,它们指的是编译器将推断程序部分的静态类型的程度。编程语言都有一定形式的类型推断。有些语言的类型推断比其他语言更强大,如ML和Haskell具有隐式类型,即不需要声明类型(或者根据语言和扩展使用情况只需要很少的类型声明)。Java和Ada则具有非常显式的类型,需要不断地声明类型。所有这些语言都拥有相对较强的静态类型系统(与C和C ++相比)。


14

这段内容摘自Scott的《编程语言实践》第三版第291页,内容如下:

类型检查是确保程序遵守语言类型兼容性规则的过程。违反规则被称为类型冲突。如果一种语言禁止在不支持该操作的任何对象上应用任何操作,并且可以强制执行语言实现,则称其为强类型。如果它是强类型的且可以在编译时进行类型检查,则称之为静态类型。从严格意义上讲,很少有语言是静态类型的。实际上,该术语通常适用于大多数类型检查可以在编译时完成的语言,并且其余部分可以在运行时完成。

几个例子:Ada是强类型语言,大多数情况下是静态类型的(某些类型约束必须在运行时检查)。Pascal实现也可以在编译时执行大部分类型检查,尽管该语言并不完全是强类型的:未标记的变异记录(将在7.3.4节中讨论)是其唯一的漏洞。C89比其前身方言更加强类型,但仍然比Pascal强度要低得多。它的漏洞包括联合体、具有可变数量的子程序。

简单来说,静态/动态类型是指类型检查发生的时间:静态类型是在编译时进行检查,而动态语言是在运行时进行检查。 同样,强/弱类型指的是语言在执行其类型系统时的严格程度。
参数的数量、指针和数组的互操作性(将在第7.7.1节中讨论)往往不受C实现的运行时检查限制。

动态(运行时)类型检查是一种迟绑定形式,并且倾向于出现在将其他问题推迟到运行时的语言中。 Lisp和Smalltalk是动态(尽管强大)类型的。 大多数脚本语言也是动态类型; 有些(例如Python和Ruby)是强类型的。 具有动态作用域的语言通常是动态类型(或根本不需要类型):如果编译器无法确定名称引用的对象,则通常也无法确定对象的类型。

因此,在简单的术语中,静态/动态类型是指类型检查发生的时间:静态类型在编译时进行检查,动态语言在运行时进行检查。 同样,强/弱类型是指语言在执行其类型系统时的严格程度。

我试图将Scott的描述转化为一个漂亮的图表,如下所示:

The Static/Dynamic - Strong/Weak Typing Plane


12
  • 静态类型语言是指在编译时进行类型检查的语言。这也意味着在静态类型语言中,每个变量都有一个类型,并且在其生命周期内不会改变。
  • 相反地,动态类型语言是指在运行时进行类型检查的语言,并且在编译时没有类型检查。这也意味着在动态类型语言中,可能存在与变量相关联的类型或可能不存在,如果存在类型,则可以是像 JS 中的“var”一样的通用类型,适用于字符串和数字。
  • 实现动态类型检查语言的方式通常是将每个运行时对象与包含其类型信息的类型标签(即对类型的引用)关联起来。 运行时类型信息(RTTI)也可用于实现动态分派、后期绑定、向下转型、反射等类似功能。
  • 即使语言是静态类型的,它仍然可能具有某些动态类型的特性,这基本上意味着在运行时也有某种类型检查。这在类型转换中很有用。
  • 许多有用的和常见的编程语言功能无法静态检查,例如向下转型。因此,许多语言都将同时使用静态和动态类型检查;静态类型检查程序会验证它所能验证的内容,而动态检查程序会验证其余的内容。
  • 一些语言允许写不安全类型的代码。例如,在 C 中,程序员可以自由地在任何两个具有相同大小的类型之间进行强制转换。
  • “静态”类型语言的优点是:
    • 由于大部分类型检查都在编译时完成,因此解释器或运行时可以以最高速度运行,无需担心类型问题。
    • 这导致了更少数量的与类型相关的运行时异常或错误,因为大部分类型检查都在编译时完成。
  • “动态”类型语言的优点是:
    • 快速原型设计非常有用,因为开发人员不需要理解类型系统,所以可以松散地创建变量并运行它,这导致非常快速的原型设计。
  • 静态类型和动态类型语言列表:
    • 静态类型:
      • Java
      • C(C是静态类型语言,但与Java相比“强类型”较低,因为它允许更多的隐式转换)
      • C++
      • C#
    • 动态类型:
      • Perl
      • PHP
      • Python
      • JavaScript
      • Ruby
  • 类型检查是一项重要的安全功能。假设没有类型检查,一个方法接受一个名为“BankAccount”的对象,该对象有一个称为“creditAccount(BankAccountDetails)”的方法。现在,在运行时如果没有类型检查,那么我可以传递一个属于我自己类的对象,该对象具有同样的方法“creditAccount(BankAccountDetails)”,并且它将被执行。考虑到我们正在谈论面向对象的语言,因为OOP支持“多态”,而这里我们讨论的就是“多态”。因此,基本上一个面向对象的语言(这基本上意味着它支持“多态”),如果没有强类型检查,可能会导致安全问题。
  • 强类型与弱类型语言

    • 强类型语言是指在有损精度的情况下不允许隐式转换。例如,在Java中,您可以将“int to long”进行强制转换,因为没有损失精度,但不能“隐式地”将“long to int”进行转换,因为会有精度损失。相反,在弱类型语言中,即使有损失精度,也允许隐式转换。
    • 我认为,如果动态类型语言在运行时不允许有损精度的隐式转换,则它也可以是强类型语言。

    进一步阅读资料


    你引用了“现在如果没有类型检查,那么我可以传递一个具有相同方法‘creditAccount(BankAccountDetails)’的自己的类的对象”-- 如果你已经绕过了可能阻止你传递对象的机制,那么在静态类型语言的情况下,类型检查如何阻止你调用该方法呢? - Aseem Yadav
    @AseemYadav,你说的“如果你已经超越了可能阻止你传递对象的机制”是什么意思? - hagrawal7777
    正如您所提到的,这是一个重要的安全功能,并且您可以通过相同的方法传递自己类的对象,因此对我来说,它似乎只在您试图侵入别人的代码时才会存在漏洞,如果您是在谈论属于您自己的代码,则更多地涉及性能问题而不是与安全相关的问题,不是吗? - Aseem Yadav
    这并不涉及性能方面的问题,你必须从多态的上下文中看待它,然后你才能理解它的安全性方面,我在同一段落中已经提到了这一点。 - hagrawal7777

    5
    我认为其他同事在解释静态类型和动态类型的区别方面做得很好。但是关于强类型和弱类型,应该说存在不同的理解和观点。
    以下是两个例子:
    一些人认为Haskell是强类型的,因为您不允许进行任何类型转换。
    另一些人(例如Dario的观点)认为,一种允许有意将字符串隐式转换为数字的语言是弱类型的,但甚至其他人称其为鸭子类型。
    这两个陈述都强调了类型系统不同的方面,而不是相反的极端。因此,我赞同Ramsey先生的观点,不要使用“强”和“弱”来区分类型系统。

    5
    在编程中,数据类型是一种分类,它告诉变量将保存什么类型的值以及可以在这些值上执行哪些数学、关系和逻辑操作而不会出错。
    在每种编程语言中,为了最小化出错的机会,在程序执行之前或期间都会进行类型检查。 根据 类型检查的时间,编程语言分为2种类型:静态类型语言和动态类型语言。
    此外,根据是否发生隐式类型转换,编程语言分为2种类型:强类型语言和弱类型语言。

    typing- static vs dynamic, strong vs weak

    静态类型:

    • 类型检查在编译时完成

    • 在源代码中,在变量声明时,必须明确指定该变量的数据类型。因为如果在源代码中指定了数据类型,则在编译时将该源代码转换为机器代码并进行类型检查

    • 这里数据类型与变量相关联,如 int count。而且这种关联是静态或固定的

    • 如果我们尝试通过将其他数据类型的值(int count = “Hello”)赋给已声明的变量(int count)来更改已声明变量的数据类型,则会出错

    • 如果我们尝试通过重新声明已声明的变量(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 
    
    • 由于类型检查和类型错误检测在编译时完成,因此在运行时不需要进一步的类型检查。因此程序变得更加优化,执行速度更快。

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

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

    动态类型:

    • 类型检查在运行时完成

    • 在源代码中,在变量声明时,不需要显式指定该变量的数据类型。因为在运行时进行类型检查时,语言系统会从分配给该变量的值的数据类型确定变量类型

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

    • 我们可以通过将其他数据类型的值(foo = "Hi")分配到已经声明的变量(var foo = 10)中来轻松更改数据类型,没有错误

    • 我们可以通过使用其他数据类型的值(var foo = true)重新声明已经声明的变量(var foo = 10)来轻松更改数据类型,没有错误

    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 
    
    • 由于类型检查和类型错误检测是在运行时进行的,所以程序变得不够优化,导致执行速度变慢。虽然如果它们实现了JIT编译器,这种类型的语言的执行可能会更快。

    • 如果我们想轻松编写和执行代码,那么这种类型的语言是更好的选择,但在这里我们可能会遇到运行时错误。

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

    强类型:

    • 数据类型相关的规则和限制严格遵守

    • 从一个数据类型转换为另一个数据类型必须显式地完成,不能进行隐式类型转换

    # in python, "5" cannot automatically get converted to 5
    pybar = "5"
    
    print(10 + pybar)     # error, no `+` operation between `int` and `str` 
    
    • 类型检查可以在编译时或运行时完成。这意味着强类型语言可以是静态类型或动态类型

    • 例如:Python、Java、Ruby、C#等。

    弱类型:

    • 数据类型相关的规则和限制被宽松地维护。

    • 从一个数据类型到另一个数据类型的转换可能会隐式发生。

    • 如果我们在两个不匹配的数据类型之间执行某些操作,则此类语言可能不会抛出错误。相反,弱类型语言将应用其自己的规则进行隐式类型转换,并返回一些结果。

    jsbar = "5";
    
    alert(10 + jsbar);  /* "105", no error as javascript implicitly coerces Number 10 
    to String "10", so that it can be concatenated with other operand jsbar i.e. "5" */
    
    • 类型检查可以在编译时或运行时完成。这意味着弱类型语言可以是静态类型或动态类型。

    • 例如:JavaScript、C、C++、PHP等。


    1
    从Addison Wesley的《面向对象的分析与设计》第三版第66页:
    强类型和弱类型以及静态类型和动态类型的概念是完全不同的。强类型和弱类型是指类型一致性,而静态类型和动态类型是指名称绑定到类型的时间。静态类型(也称为静态绑定或早期绑定)意味着在编译时所有变量和表达式的类型都是固定的;动态类型(也称为晚期绑定)意味着在运行时不能确定所有变量和表达式的类型。一种语言可能既是强类型又是静态类型(Ada),既是强类型但同时支持动态类型(C++,Java),也可能是无类型但同时支持动态类型(Smalltalk)。

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接