【问题标题】:How do I initialize a final class property in a constructor?如何在构造函数中初始化最终类属性?
【发布时间】:2017-08-09 10:44:24
【问题描述】:

在 Java 中你可以这样做:

class A {    
    private final int x;

    public A() {
        x = 5;
    }
}

在 Dart 中,我尝试过:

class A {    
    final int x;

    A() {
        this.x = 5;
    }
}

我得到两个编译错误:

必须初始化最终变量“x”。

'x' 不能用作 setter,因为它是 final。

有没有办法在 Dart 的构造函数中设置最终属性?

【问题讨论】:

    标签: constructor dart final


    【解决方案1】:

    不能在构造函数主体中实例化最终字段。有一个特殊的语法:

    class Point {
      final num x;
      final num y;
      final num distanceFromOrigin;
    
      // Old syntax
      // Point(x, y) :
      //   x = x,
      //   y = y,
      //   distanceFromOrigin = sqrt(pow(x, 2) + pow(y, 2));
    
      // New syntax
      Point(this.x, this.y) :
        distanceFromOrigin = sqrt(pow(x, 2) + pow(y, 2));
    }
    

    【讨论】:

    • 关于关注点分离,我想补充一下familiarise with final 及其在构造函​​数中的含义。 final 是指只分配一次;因此 Drat 提供了它的接口/糖来更快地为这些属性赋值(常见模式)。同时,如果您要更改“x”属性的值,则不要设置为final(或者,如果您需要更多操作/操作来设置最终结果,您可以查看@rkj 的factory 建议)
    • 您好,如果 Point 类继承了另一个类,并且必须使用 super(x,y) 调用其父构造函数并实例化像 distanceFromOrigin 这样的最终字段,该怎么办?
    • 我找到了我的问题的答案:只需将两条指令用昏迷分开。请注意,super() 调用必须是最后一件事。
    • @OliverDixon 我发现这是有效的dart class Point { final num x; final num y; final num distanceFromOrigin; final num distanceFromOrigin2; Point(this.x, this.y) : distanceFromOrigin = sqrt(pow(x, 2) + pow(y, 2)), distanceFromOrigin2 = sqrt(pow(x, 2) + pow(y, 2)); }
    • 并在其末尾添加, super(key: key)
    【解决方案2】:

    您可以使用构造函数中的this. 语法使其更短(在https://www.dartlang.org/guides/language/language-tour#constructors 中描述):

    class Point {
      final num x;
      final num y;
      final num distanceFromOrigin;
    
      Point(this.x, this.y)
          : distanceFromOrigin = sqrt(pow(x, 2) + pow(y, 2));
    }
    

    如果你有一些更复杂的初始化你应该使用工厂构造函数,代码变成:

    class Point {
      final num x;
      final num y;
      final num distanceFromOrigin;
    
      Point._(this.x, this.y, this.distanceFromOrigin);
    
      factory Point(num x, num y) {
        num distance = distanceFromOrigin = sqrt(pow(x, 2) + pow(y, 2));
        return new Point._(x, y, distance);
      }
    }
    

    【讨论】:

    • 迟到的问题,但是 factory 关键字在这里做了什么。我以为它是用来决定是返回现有对象还是新对象,但你总是在这里返回一个新对象
    • 需要工厂,因为不能立即创建 Point 对象 - 我们首先需要计算 distance,然后我们才准备好初始化所有最终变量。
    • factory 关键字告诉您需要自己构造返回值(与不使用相反,它将为您创建)。
    【解决方案3】:

    这里是对最终类变量的初始化方法的简化总结。

    class MyClass {
      final int x; //     <-- initialize this
    }
    

    初始化值

    class MyClass {
      final int x = 'hello'.length;
    }
    

    如果初始化只能在运行时完成,您只能使用final。否则,static const 更好:

    class MyClass {
      static const int x = 0;
    }
    

    初始化程序正式

    class MyClass {
      MyClass(this.x);
      final int x;
    }
    

    这是最常用的方法。

    初始化列表

    class MyClass {
      MyClass(int x) 
        : _x = x;
      final int _x;
    }
    

    当您想保持字段私有时,这很有用。

    默认参数值

    对于未命名的参数,您可以用方括号 ([]) 将参数括起来,对于已命名的参数,您可以使用大括号 ({}) 括起来,然后给它一个默认值。

    class MyClass {
      MyClass({this.x = 0});
      final int x;
    }
    

    如果您想使参数可选,这很有用。

    你也可以用初始化列表来完成同样的事情:

    class MyClass {
      MyClass({int? x}) 
        : _x = x ?? 0;
      final int _x;
    }
    

    延迟初始化

    class MyClass {
      MyClass(String? a) {
        x = a?.length ?? 0;
      }
      late final int x;
    }
    

    如果您需要进行比初始化列表中允许的更复杂的初始化,这很有用。例如,我在 Flutter 中初始化手势识别器时就这样做了。

    延迟初始化

    使用late 的另一个优点是它在您访问该值之前不会初始化该值。

    class MyClass {
      late final int x = _doHeavyTask();
      int _doHeavyTask() {
        var sum = 0;
        for (var i = 0; i < 100000000; i++) {
          sum += 1;
        }
        return sum;
      }
    }
    

    如果您有一个繁重的计算,而您只想在绝对需要时调用它,这将非常有用。

    这不会初始化x

    final myClass = MyClass();
    

    但这确实会初始化x:

    final myClass = MyClass();
    final value = myClass.x;
    

    【讨论】:

      【解决方案4】:

      我遇到了类似的问题:我试图从构造函数初始化一个final 字段,同时调用一个超级构造函数。你可以想到下面的例子

      class Point2d {
        final int x;
        final int y;
      
        Point2d.fromCoordinates(Coordinates coordinates)
            : this.x = coordinates.x,
              this.y = coordinates.y;
      }
      
      class Point3d extends Point2d {
        final int z;
      
        Point3d.fromCoordinates(Coordinates coordinates)
            :this.z = coordinates.z,
              super.fromCoordinates(coordinates);
      }
      
      /// Demo class, to simulate constructing an object
      /// from another object.
      class Coordinates {
        final int x;
        final int y;
        final int z;
      }
      

      显然这行得通。您可以使用上述语法初始化您的最终字段(检查 Point3d 的构造函数),它工作得很好!

      像这样运行一个小程序来检查:

      void main() {
        var coordinates = Coordinates(1, 2, 3);
        var point3d = Point3d.fromCoordinates(coordinates);
        print("x: ${point3d.x}, y: ${point3d.y}, z: ${point3d.z}");
      }
      

      应该打印x: 1, y: 2, z: 3

      【讨论】:

        【解决方案5】:

        我在这里遇到了一个难题,我想初始化一个没有项目的最终 List,并在构造函数中定义一个 Stream(如本例中的 distanceFromOrigin)。

        我无法用以下任何答案做到这一点,但我将它们混合在一起并且它起作用了。

        例子:

        class MyBloc {
          final BehaviorSubject<List<String>> itemsStream;
          final List<String> items = [];
        
          MyBloc() : this.itemsStream = BehaviorSubject<List<String>>.seeded([]) {
            items.addAll(List.generate(20, (index) => "Hola! I'm number $index"));
            itemsStream.add(items);
          }
        }
        

        【讨论】:

          【解决方案6】:
          class A{
            final int x;
            
            A(this.x){
            }
            
          }
          

          【讨论】:

            猜你喜欢
            • 2022-11-23
            • 2015-09-11
            • 2014-08-23
            • 2019-07-31
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多