我不理解Dart中的“Null”是什么意思。

6

我不了解 Dart 中的 Null 是什么意思。

在 C# 语言中,null 是一个字面量。 null 是引用类型变量的默认值。

C# 中的 null 没有类型。通过点 . 不可能访问 null 的成员。

示例:

String s = null; // the default value of reference-type variable
null.GetType(); // Operator '.'  cannot be applied to operand of the type <null>

在Dart中,null有一个类型Null。但是在Dart中Null类型的实例不是bottom类型。
根据类型继承,在Dart中它是如何工作的?
// How the `null` value with type of `Null`
// can be assigned to a `String` type?
String s = null;

这段代码在Dart编译器中产生了奇怪的警告。
"Tests for null should be done with '== null'".
print(s is NUll);

但我不想“测试null”。我想测试Null。这不一样。
为什么Dart编辑器会出现这个奇怪的警告?
我想测试Dart类型系统的工作原理,但编辑器给了我这个消息。
我不理解它,因为我无法理解Null类型如何可以同时:
1.成为所有类型的子类型
2.不是所有类型的子类型
期待开发者的回答。
是否存在与面向对象编程相反的魔法(在Dart语言规范中未记录)以确定不兼容类型为兼容类型?
附言:
Dart中的Null类型是完整的类,因此它是一个“常规类型”。
我没有找到官方文件说明相反的内容(即Null类型不是一个“常规”类型,而是一个“底部”类型)。
这里是我的声明证明。
class Null {
  factory Null._uninstantiable() {
    throw new UnsupportedError('class Null cannot be instantiated');
  }

  /** Returns the string `"null"`. */
  String toString() => "null";
}

您可以看到Null不是一个bottom类型。

在这里,您可以看到null不是一个bottom类型,而是一个Null类型。

print(null.runtimeType == Null);

true

附言:

我不明白为什么有些人不理解问题,还举出矛盾的例子。

The static type of null is bottom.

不正确。 null 的类型是 Null 类型,而不是 bottom 类型。

我已经提供了证据:

  • null.runtimeType == Null,但不是 bottom 类型
  • Null 类不是 bottom 类型(请查看 SDK 中的源代码)。

附言:

为什么所有这些答案都不能对应于实际情况(运行时)的结论?

或者说对手不是开发人员?

或者说 Dart 运行时不遵循语言规范,而对手认为(或不知道?)这条规则没有被遵守?

我认为 null 必须有一个类型为 _Null 的类型,在源代码中未声明,但在虚拟机中 创建内部

在这种情况下,我无法说 _Null 不是 bottom 类型,因为这是隐藏在虚拟机中的。

谁是正确的?我还是你?

2个回答

8

规范中可以得知:

Null

保留字null表示空对象。

nullLiteral: null

;

空对象是内置类Null的唯一实例。尝试实例化Null会导致运行时错误。对于一个类来说,试图扩展或实现Null是编译时错误。在空对象上调用方法会产生NoSuchMethodError,除非该方法由类Null显式实现。

null的静态类型为bottom。

使用bottom而不是Null的决策允许null被分配到任何位置而不会受到静态检查器的抱怨。

关于bottom的文档很少,我找不到太多参考资料。然而,当规范讨论void时,提到了:

bottom是所有类型的子类型

因此,回答您的问题,Null类型没有什么神奇的地方,但是null文字具有bottom类型,这意味着它是所有类型的子类型。

由于nullNull的唯一实例,因此检查某个值是否== null而不是is Null更为准确,这可能就是编辑器给出警告的原因。


在编译时,分析器将null视为“底部”仅用于消除类型警告...它的runtimeType是Null,并且它是Null类的一个实例。 - Ganymede
@Ganymede 但是Null并不是一个字符串。它是如何工作的?非字符串如何分配给字符串。 - mezoni
3
你混淆了类型和类的概念。null的类是Null,它的类型是bottom。类和类型是完全不同的概念,它们之间没有任何关系。在面向对象编程中,类定义表示,而类型定义行为。 - Jörg W Mittag
很有趣。我以前从未听说过 bottom 这种类型。 - Günter Zöchbauer
1
也许你忘记了 Dart 不仅有“编译时检查器”,还有“运行时类型检查器”。在运行时,null 不是 bottom 的值(如编译时所假设的),而是 Null 类型的值。在语言规范中,Null 并没有被描述为“bottom”类型(所有类型的子类型)。在实践中(参见源代码),Null 类被声明为常规类型。我不是在问编译过程,而是在问在运行时,如何将常规类型 Null 的值分配给不兼容的类型? - mezoni
显示剩余9条评论

3
NullObject的子类型,由于Dart是一种动态语言,因此将nullNull的实例)分配给String或任何其他类型的变量是有效的。 静态类型检查必须有一个特殊规则,以不对将null分配给另一种类型的变量产生错误或警告。
检查s is Null的结果为true,但Dart风格指南似乎强制要求s == null,原因未知(可能更快)。

2
Dart不是一种动态语言。Dart是一种具有可选类型的语言。这与动态语言(例如Ruby)不同。在Dart中,将Null实例分配给String类型或任何其他类型的变量是无效的。因为Null不是String的子类型。这是神奇的。底部类型的值可以分配给任何类型。这是正确的。但是,Null的值不能分配给任何类型。魔法规则并不严谨。底部不是超类型而是子类型。也就是说,bottom是Null:true。Null是bottom:false。Null是String:false。 - mezoni
Java和C#不是动态语言。但是可以通过编译器服务来模拟“eval”功能并在这些语言中执行字符串。但是,由于它们不是动态的,您永远无法在现有声明中添加/删除声明。在非动态语言中,不可能向现有类型添加成员。您只能添加新的声明,但这并不需要将这些语言称为动态语言,因为这可以通过编译器服务而不是通过修改现有类型来完成。 - mezoni

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