dart 中的 dynamic 和 Object 有什么区别?

What is the difference between dynamic and Object in dart?

它们看起来都可以在相同的情况下使用。在类型检查等方面是否有不同的表示或不同的微妙之处?

来自 Dart Programming Language Specification, 3rd EditionType dynamic 部分指出:

Type dynamic has methods for every possible identifier and arity, with every possible combination of named parameters. These methods all have dynamic as their return type, and their formal parameters all have type dynamic. Type dynamic has properties for every possible identifier. These properties all have type dynamic.

这意味着您不会通过在 dynamic 类型变量上调用任何方法而收到警告。对于类型为 Object 的变量,情况并非如此。例如:

dynamic a;
Object b;

main() {
  a = "";
  b = "";
  printLengths();
}

printLengths() {
  // no warning
  print(a.length);

  // warning:
  // The getter 'length' is not defined for the class 'Object'
  print(b.length);
}

我认为,在运行时,您应该看不出任何区别。

要补充 Alexandre 关于实际差异的回答,两者之间也存在语义差异,使用正确的差异将有助于更好地向其他程序员传达您的意图。

当你使用 Object 时,你是在说你知道你正在使用的类型,它是 Object。例如:

int getHashCode(Object obj) {
  return obj.hashCode;
}

因为 hashCodeObject 上的 属性 我们使用 Object 作为参数类型来指定函数可以接受任何类型 Object .

另一方面,使用 dynamic 意味着 Dart 系统无法正确表达您要使用的类型:

void setEmail(dynamic email) {
  if (email is Email) {
    _email = email;
  } else if (email is String) {
    _email = new Email.fromString(email);
  }
}

由于 Dart 目前不支持联合类型,因此无法表达类型 Email | String 所以我们不得不使用 dynamic 来接受所有类型,然后只处理类型是我们感兴趣的类型。

关于 dynamic 的另一个观点是,它并不是真正的类型 - 它是一种关闭类型检查并告知静态类型系统 "trust me, I know what I'm doing" 的方法。编写 dynamic o; 声明了一个未键入的变量 - 它被标记为 "not type-checked".

当您写 Object o = something; 时,您是在告诉系统它不能假设任何有关 o 的信息,除了它是一个 Object。您可以调用 toStringhashCode,因为这些方法是在 Object 上定义的,但是如果您尝试执行 o.foo(),您将收到警告 - 它看不到您可以做到这一点,因此它会警告您您的代码 可能 错误。

如果你写 dynamic o = something 你是在告诉系统不要假设任何东西,也不要检查任何东西。如果你写 o.foo() 那么它不会警告你。你告诉它 "anything related to o is OK! Trust me, I know what I'm doing",所以它认为 o.foo() 没问题。

能力越大,责任越大 - 如果您禁用变量的类型检查,它会返回到您身上以确保您没有做错任何事情。

此外,我注意到扩展方法不能正确地与 dynamic 一起工作,但可以与 Object 一起工作。


// I used to have the extension on dynamic and had 
// problems that didn't occur when using the same extension on Object

extension UtilExtensions on Object {   

  bool get isStringNotEmpty => this is String && (this as String).isNotEmpty;
  String get asStringNotEmpty => isStringNotEmpty ? this as String : null;

  bool get isIntNotZero => this is int && (this as int) != 0;
  int get asIntNotZero => isIntNotZero ? this as int : null;

  Map<String, Object> get asPair {
    if (this != null && this is Map) {
      return (this as Map).cast<String, Object>();
    }

    return null;
  }

  Map<String, Object> get asFullPair {
    if (this != null && this is Map) {
      var ret = (this as Map).cast<String, Object>();

      for (var key in ret.keys) {
        var val = ret[key];

        if (val is Map) {
          ret[key] = val.asFullPair;
        }
      }

      return ret;
    }

    return null;
  }
}

dynamic 不是类型,它只是禁用类型检查。 Object 是所有不可空类型的 'union',类型检查规则仍然适用。

比较这两种情况:

案例 1(动态)

// a 'dynamic' variable can be assigned value of any type
dynamic a = 2;

// assign 'dynamic' value to any variable and code checker will not complain
int b = a;
// even when there is a bug
String c = a;

案例 2(对象)

// It is OK to assign a 'int' value to an 'Object' variable, because 'int' is a subtype of 'Object'
Object a = 2;

// will get type error: "A value of type 'Object' can't be assigned to a variable of type 'int'"
int b = a;

// typecast is required when assign a 'Object' value to a variale of one of its subtypes.
int c = a as int;

您无法使用 Object 访问 class 的方法和属性,但可以使用 dynamic 访问。

示例:

class MyClass{
    myFunction() => print("This works");        
}

void main(){
dynamic a = new MyClass();
Object b = new MyClass();
a.myFunction(); // prints without error
b.myFunction(); // error comes saying myFunction isn't defined for b
}

加上lrn给出的答案, 同样值得注意的是,对于启用 Null 安全的 Dart 版本 2.12 和更高版本,声明为 dynamic 的变量可以是 null,而变量类型 Object 不能是 null.