我正在学习Dart,我想有一种类似于Kotlin中的let的方法。
我想把它用作:
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?或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 (根据我最初的观察:我不能返回null)。
似乎没有完全执行无效安全措施。
edit2:似乎只有在推断类型时才是合法的。例如:
letNonNull(null, (it) => "bogus", () => null); // legal
letNonNull<String, String>(null, (it) => "bogus", () => null); // illegal我本来希望推断的类型是非空的.
发布于 2021-05-18 09:10:09
您不能返回null,因为O可能被绑定到非空类型。类型变量不是“总是不可空的”,但它们始终是潜在的非空变量。返回类型为O的函数返回的内容必须对所有可能的O绑定有效,即使绑定到非空类型时也是如此。或者当它被绑定到Never时。这意味着唯一可能有效返回的类型是O本身,而null没有类型O。
如果希望始终能够返回null,则必须使let的返回类型成为O?。这使得它始终是可空的,即使O本身是不可空的。在这种情况下,我会将O限制为不可空,方法是给它一个extends Object的界。
另一种方法是,如果null是一个有效的返回值,则返回null,如果它不是,则抛出(因此不必返回任何内容,因为您没有任何要返回的东西),但是您的方法不适用于类型系统。试着改变
if (null is O) {
return null;
}至
O? nullReturn = null;
if (nullReturn is O) {
return nullReturn;
}如上所述,您可以返回的唯一类型是O,因此您希望值null具有O类型。您可以执行if (null is O) return null as O; (甚至只是return null as O;,并依赖于cast中的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无效,因为O的类型不可为空。
类型系统不知道如何使用or函数参数,它只是检查提供的O的实际值是否是O Function()参数类型的一个适当的子类型。函数体的类型检查确保它只能用于结果可以接受的位置。这就是不允许在上面返回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");
}
}https://stackoverflow.com/questions/67577995
复制相似问题