如何最好地转换触发 "The non-nullable variable must be assigned" 错误的旧 Dart 代码?
How best to convert old Dart code that triggers "The non-nullable variable must be assigned" error?
采用以下非空安全 Dart 代码:
static String appBarShiftTitleString(int fromEpochSeconds) {
String monthWord;
String dayWord;
DateTime dt = DateTime.fromMillisecondsSinceEpoch(fromEpochSeconds * 1000);
switch (dt.month) {
case 1:
monthWord = "Jan";
break;
case 2:
monthWord = "Feb";
break;
case 3:
monthWord = "Mar";
break;
case 4:
monthWord = "Apr";
break;
case 5:
monthWord = "May";
break;
case 6:
monthWord = "Jun";
break;
case 7:
monthWord = "Jul";
break;
case 8:
monthWord = "Aug";
break;
case 9:
monthWord = "Sep";
break;
case 10:
monthWord = "Oct";
break;
case 11:
monthWord = "Nov";
break;
case 12:
monthWord = "Dec";
break;
}
switch (dt.weekday) {
case 1:
dayWord = "Mon";
break;
case 2:
dayWord = "Tue";
break;
case 3:
dayWord = "Wed";
break;
case 4:
dayWord = "Thu";
break;
case 5:
dayWord = "Fri";
break;
case 6:
dayWord = "Sat";
break;
case 7:
dayWord = "Sun";
break;
}
return dayWord + ' ' + monthWord + ' ' + dt.day.toString();
}
Android Studio 表示,“不可为 null 的局部变量 'dayWord' 必须先赋值才能使用。”
我理解错误并发现我可以像这样简单地修改方法的前两行:
String monthWord = "error!";
String dayWord = "error!";
这样,我就满足了语言规则,如果我们达到了变量没有被赋值的不可能的情况,那就很明显了。
虽然这看起来很老套......所以在这些类型的场景中,将此代码转换为空安全的优雅和正确的方法是什么?如果有多种方法,那么优缺点是什么?
谢谢!
一般来说,您有以下几种选择:
1。将变量初始化为一些非空标记值和 assert
之后:
String monthWord = '';
// ...
switch (dt.month) {
// ...
}
assert(monthWord.isNotEmpty);
如果您忽略在 switch
.
中处理它的情况,这将导致调试版本在运行时抛出 AssertionError
2。使变量可为空并使用空断言运算符:
String? monthWord;
// ...
switch (dt.month) {
// ...
}
monthWord!;
// Since `monthWord` is a local variable, it will now be promoted to a
// non-nullable `String` type.
如果您忽略将变量设置为非空值,这将在所有构建类型中引发 TypeError
。
3。使变量 late
将变量声明为 late
表示您承诺变量将在读取之前进行初始化。编译器将生成运行时检查,以验证在您尝试访问变量时变量是否已初始化。如果您忽略设置变量,这将在所有构建类型中抛出 LateInitializationError
。
4。添加一个抛出
的 default
案例
如果您的所有 case
都设置了局部变量,则添加抛出的 default
情况允许编译器推断如果 [=16= 之后的代码必须始终设置该变量] 语句到达:
String monthWord; // No explicit initialization required!
// ...
switch (dt.month) {
case 1:
monthWord = "Jan";
break;
// ... etc. ...
default:
throw AssertionError('Unhandled case: ${dt.month}');
}
// The compiler now can deduce that `monthWord` is guaranteed to be
// initialized.
(请注意,如果您在 enum
类型。对于 enum
s,编译器和分析器可以确定您的 case
s 是否详尽无遗,如果您不小心遗漏任何情况,将生成分析警告。)
至于使用哪种方法,主要是个人喜好问题。它们在大多数情况下都是等价的,因为它们会导致运行时错误。我个人会选择 #1 (assert
) 或 #4 (default
case) 以避免在发布版本中进行不必要的检查。
在您的特定示例中,我也将分别使用 DateTime.month
和 DateTime.day
作为 List
月份和日期名称的索引:
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
assert(months.length == 12);
assert(days.length == 7);
var monthWord = months[dt.month - 1];
var dayWord = days [dt.day - 1];
采用以下非空安全 Dart 代码:
static String appBarShiftTitleString(int fromEpochSeconds) {
String monthWord;
String dayWord;
DateTime dt = DateTime.fromMillisecondsSinceEpoch(fromEpochSeconds * 1000);
switch (dt.month) {
case 1:
monthWord = "Jan";
break;
case 2:
monthWord = "Feb";
break;
case 3:
monthWord = "Mar";
break;
case 4:
monthWord = "Apr";
break;
case 5:
monthWord = "May";
break;
case 6:
monthWord = "Jun";
break;
case 7:
monthWord = "Jul";
break;
case 8:
monthWord = "Aug";
break;
case 9:
monthWord = "Sep";
break;
case 10:
monthWord = "Oct";
break;
case 11:
monthWord = "Nov";
break;
case 12:
monthWord = "Dec";
break;
}
switch (dt.weekday) {
case 1:
dayWord = "Mon";
break;
case 2:
dayWord = "Tue";
break;
case 3:
dayWord = "Wed";
break;
case 4:
dayWord = "Thu";
break;
case 5:
dayWord = "Fri";
break;
case 6:
dayWord = "Sat";
break;
case 7:
dayWord = "Sun";
break;
}
return dayWord + ' ' + monthWord + ' ' + dt.day.toString();
}
Android Studio 表示,“不可为 null 的局部变量 'dayWord' 必须先赋值才能使用。”
我理解错误并发现我可以像这样简单地修改方法的前两行:
String monthWord = "error!";
String dayWord = "error!";
这样,我就满足了语言规则,如果我们达到了变量没有被赋值的不可能的情况,那就很明显了。
虽然这看起来很老套......所以在这些类型的场景中,将此代码转换为空安全的优雅和正确的方法是什么?如果有多种方法,那么优缺点是什么?
谢谢!
一般来说,您有以下几种选择:
1。将变量初始化为一些非空标记值和 assert
之后:
String monthWord = '';
// ...
switch (dt.month) {
// ...
}
assert(monthWord.isNotEmpty);
如果您忽略在 switch
.
AssertionError
2。使变量可为空并使用空断言运算符:
String? monthWord;
// ...
switch (dt.month) {
// ...
}
monthWord!;
// Since `monthWord` is a local variable, it will now be promoted to a
// non-nullable `String` type.
如果您忽略将变量设置为非空值,这将在所有构建类型中引发 TypeError
。
3。使变量 late
将变量声明为 late
表示您承诺变量将在读取之前进行初始化。编译器将生成运行时检查,以验证在您尝试访问变量时变量是否已初始化。如果您忽略设置变量,这将在所有构建类型中抛出 LateInitializationError
。
4。添加一个抛出
的default
案例
如果您的所有 case
都设置了局部变量,则添加抛出的 default
情况允许编译器推断如果 [=16= 之后的代码必须始终设置该变量] 语句到达:
String monthWord; // No explicit initialization required!
// ...
switch (dt.month) {
case 1:
monthWord = "Jan";
break;
// ... etc. ...
default:
throw AssertionError('Unhandled case: ${dt.month}');
}
// The compiler now can deduce that `monthWord` is guaranteed to be
// initialized.
(请注意,如果您在 enum
类型。对于 enum
s,编译器和分析器可以确定您的 case
s 是否详尽无遗,如果您不小心遗漏任何情况,将生成分析警告。)
至于使用哪种方法,主要是个人喜好问题。它们在大多数情况下都是等价的,因为它们会导致运行时错误。我个人会选择 #1 (assert
) 或 #4 (default
case) 以避免在发布版本中进行不必要的检查。
在您的特定示例中,我也将分别使用 DateTime.month
和 DateTime.day
作为 List
月份和日期名称的索引:
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
assert(months.length == 12);
assert(days.length == 7);
var monthWord = months[dt.month - 1];
var dayWord = days [dt.day - 1];