dart真的是100%纯面向对象的编程语言吗

owfi6suc  于 2023-06-19  发布在  其他
关注(0)|答案(2)|浏览(91)

当我在学习dart的基础知识时,它提到dart是100%纯面向对象编程语言事件基元类型,如int,double是对象,并作为引用而不是值传递。

void main() {
double number = 44;
  print('before $number');
  changeNumber(number); // with this it does not print 45
  // number++; with this it does print 45;
  print('after $number');
}

void changeNumber(double number) => number++;

输出:
45岁以前
44岁以后
我期待人们解释我,如果int是一个对象,为什么其他函数不能改变它时传递,我知道dart通过值传递数字,但为什么它这样做

gdrx4gfi

gdrx4gfi1#

我希望人们解释我,如果int是一个对象,为什么其他函数在传递时不能改变它,我知道dart通过值传递数值。
正如mousetail在他们的评论中提到的,仅仅因为某个东西是一个对象,并不意味着对象的状态是可变的。Dart中的很多类都是为了创建不可变的对象而设计的,任何类型的变化都会导致另一个对象示例。
例如,intdoubleString都是类的所有类型,其中这些类的对象是不可变的。
在示例中,number++操作意味着number = number + 1。这里定义了numberdouble)上的+运算符,以获取1,并返回一个新的double对象,其中我们得到了将1添加到number的结果。
由于我们没有操作number对象本身,所以对该对象的任何引用仍然会观察到number的原始值。
然而,我们确实将新的double示例重新分配给变量number。这就是为什么在main中工作的原因,因为变量是一个局部变量,你可以更改它引用的值。
但是函数的参数作为引用的副本发送。因此,如果我们操纵对象的内部状态,发送到函数,那么这些变化对函数的调用者来说是可见的。但是如果我们将引用/参数更改为指向其他对象,那么调用者将看不到此更改。
由于double是一个不可变的对象,我们保证这个对象的任何更改都会导致另一个对象示例,因此我们需要更改对这个新对象的引用,以便观察到更改。由于函数的参数是引用的副本,因此对该副本引用的任何更改都不能在函数本身之外观察到。

ltqd579y

ltqd579y2#

甚至像int,double这样的基本类型也是对象,并且作为引用而不是值传递。
Dart并没有真正的引用(或指针),所以这句话对理解发生了什么没有多大帮助。
函数形参可以看作是调用者的实参被赋给的局部变量。新变量得到的值与原始变量“相同”(但它不是C语言中的引用-您不能传递引用并让其他人更改它指向的内容)。
在您的示例中,当您调用changeNumber(number)时,可以将其替换为:

double number = 44;
  print('before $number');

  // changeNumber scope
  {
      var $number = number;
      // body of changeNumber function
      $number++;
  }
  
  // number++; with this it does print 45;
  print('after $number');

这就是所谓的“内联”,编译器实际上可以自动完成。无论如何,你基本上改变了$number变量的值,而不是原来的number,因为在被调用函数的作用域内是不可访问的(在上面的例子中是这样的,但编译器会阻止你在现实中使用它)。
希望你明白,number++并没有改变number变量的“内部”,它只是返回一个新的数字,然后分配给同一个变量。换句话说,这就是实际发生的事情:

number = number + 1;

正如其他人指出的那样,面向对象与此无关,因为在OOP中,对象被允许是完全不变的,而Dart中的数字是不变的。您可以在Dart 3中轻松创建不可变对象,例如:

final class ComplexNumber {
  final double real;
  final double imaginary;
  const ComplexNumber(this.real, this.imaginary);
  @override
  String toString() => 'Complex($real, $imaginary)';
}

你可以添加方法来将一个ComplexNumber添加到另一个ComplexNumber中,但是你需要返回一个新的方法,而不是改变当前的方法来保持它的不可变:

final class ComplexNumber {
  final double real;
  final double imaginary;
  const ComplexNumber(this.real, this.imaginary);
  @override
  String toString() => 'Complex($real, $imaginary)';
  operator +(ComplexNumber other) =>
      ComplexNumber(real + other.real, imaginary + other.imaginary);
}

main() {
  print(ComplexNumber(1.0, 2.4) + ComplexNumber(2.0, 3.5));
}

你可以让ComplexNumber变,但即使在现代的OOP中,人们通常更倾向于让对象不可变,因为这有助于程序的推理能力极大地提高(这可能会牺牲性能,因为改变某些东西比在每次改变时分配新对象更有效率)。

相关问题