dart - 泛型方法中的可空类型
问题描述
我正在学习 Dart,我希望有一种类似于let
Kotlin 的方法。
我想将其用作:
var variable = ...;// nullable type, for example MyClass?
var test1 = let(variable, (it) => 'non null: ${it.safeAccess()}');
// test1 type is String?
var test2 = let(variable, (it) => 'non null: ${it.safeAccess()}', or: () => 'Default value');
// test2 type is String since either way we return a String
在此示例中,变量是 的可空实例,MyClass
如果未提供回退,则输出为可空字符串,如果提供非空回退,则输出为非空字符串。
这是我写的原型:
typedef O LetCallback<I, O>(I value);
typedef O OrCallback<O>();
O let<I, O>(I? value, LetCallback<I, O> cb, {OrCallback<O>? or}) {
if (value != null) {
return cb(value);
}
if (or != null) {
return or();
}
if (null is O) {
return null;
}
throw Exception("Please provide a default non-null value");
}
Dart 抱怨我不能返回 null,但我不明白为什么它是非法的。我曾预料到这一点(以明确的语法):
var variable = ...;// nullable type, for example MyClass?
var test1 = let<MyClass, String?>(variable, (it) => 'non null: ${it.safeAccess()}');
// I=MyClass, O=String?
var test2 = let<MyClass, String>(variable, (it) => 'non null: ${it.safeAccess()}', or: () => 'Default value');
// I=MyClass, O=String
在我的预期中,编译器会将类型推断O
为String?
or String
,因此return null
只有当O
它可以为空时才是合法的。
似乎使用通用语法,引用的类型总是不可为空的。是这样吗?是语言的限制吗?是否可以编写我想要实现的目标,或者我是否被迫有两个实现?(例如let
,letNotNull
例如)
编辑:写完之后,我尝试了两种实现路线。这是我写的:
typedef O LetCallback<I, O>(I value);
typedef O OrCallback<O>();
O letNonNull<I, O>(I? value, LetCallback<I, O> cb, OrCallback<O> or) {
if (value != null) {
return cb(value);
}
return or();
}
O? let<I, O>(I? value, LetCallback<I, O> cb, {OrCallback<O>? or}) {
if (value != null) {
return cb(value);
}
if (or != null) {
return or();
}
}
出于某种原因,这是合法的:
var test = letNonNull(null, (it) => "whatever", () => null)
我曾预计() => null
回调将是编译器错误,因为O
不能为空(根据我最初的观察:我不能返回 null)。
似乎没有完全执行零安全性。
编辑2:似乎只有推断出类型才是合法的。例如:
letNonNull(null, (it) => "bogus", () => null); // legal
letNonNull<String, String>(null, (it) => "bogus", () => null); // illegal
我曾希望推断的类型是非空的......
解决方案
您无法返回,null
因为O
可能会绑定到不可为空的类型。类型变量并非“始终不可为空”,但它们始终可能不可为空。从具有返回类型的函数返回的内容O
必须对 的所有可能绑定有效O
,即使它绑定到不可为空的类型也是如此。或者当它绑定到Never
. 这意味着唯一可能有效返回的类型是O
它自己,并且null
没有 type O
。
如果您希望始终能够返回null
,则必须将返回类型let
设为 be O?
。这使得它始终可以为空,即使O
它本身不可为空。在这种情况下,我会O
通过给它一个extends Object
.
与您一样,另一种方法是返回null
ifnull
是有效的返回值,如果不是则抛出(从而避免返回任何内容,因为您没有要返回的内容),但是您的方法不适用于该类型系统。尝试改变
if (null is O) {
return null;
}
至
O? nullReturn = null;
if (nullReturn is O) {
return nullReturn;
}
如上所述,您可以返回的唯一类型是O
,因此您希望该值null
具有 type O
。您可以这样做if (null is O) return null as O;
(或者甚至只是return null as O;
依靠TypeError
演员阵容而不是自己投掷),或者您可以像这个例子一样使用类型提升来避免额外的as
.
您可能还想I
使用绑定限制为不可为空,然后I?
将参数用于let
,但不用于cb
。这确保了推断的I
类型始终不可为空。
O let<I extends Object, O>(
I? value,
O Function(I) cb,
{O Function()? or}) {
if (value != null) {
return cb(value);
}
if (or != null) {
return or();
}
O? returnNull = null;
if (returnNull is O) {
return returnNull;
}
throw ArgumentError.value(null, "or",
"Please provide a default non-null value");
}
letNotNull
还需要对类型变量进行限制:
O letNonNull<I extends Object, O>(
I? value, O Function(I) cb, O Function() or) {
if (value != null) {
return cb(value);
}
return or();
}
原因
var test = letNonNull(null, (it) => "whatever", () => null)
有效的是它推断出letNotNull<Object, String?>
,并且
letNonNull<String, String>(null, (it) => "bogus", () => null); // illegal
无效,因为 for 的类型O
不可为空。
类型系统不知道该or
函数参数将如何使用,它只是检查它是否是提供O Function()
的实际值的参数类型的正确子类型O
。函数体的类型检查确保它只能用于结果可接受的位置。这就是null
上面不允许返回的类型检查,因为该检查必须适用于所有O
可以绑定的类型。
我会考虑将let
操作定义为扩展方法,因为它会先对值进行类型推断,然后再查看回调。就像是:
extension Let<T extends Object> on T? {
R let<R>(R Function(T) callback, {R Function()? or}) {
var self = this;
if (self != null) return callback(self);
if (or != null) return or();
R? nullReturn = null;
if (nullReturn is R) return nullReturn;
throw ArgumentError.notNull("or");
}
}
推荐阅读
- node.js - 如何使用 nodeJS 在 EUC(CP51932)中进行 URL 编码
- flutter - 向 Scaffold Appbar 添加具有多个页面的点击计数器
- php - 如何使用 WP_REDIRECT 并继续父执行
- sql - 查找表中值的最大连续出现次数
- angular - 如何在Angular中动态创建子元素并从父组件传递属性
- python - 将剪辑与moviepy合并在一起
- twilio - Twilio taskRouter js sdk给出CORS错误
- r - 如何计算R中数据框中某个单词的每次出现?
- javascript - 现代相当于 for... 在循环中?
- javascript - FullCalendar 不显示 extraParams