首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >泛型方法中的可空类型

问泛型方法中的可空类型
EN

Stack Overflow用户
提问于 2021-05-17 23:03:58
回答 1查看 682关注 0票数 0

我正在学习Dart,我想有一种类似于Kotlin中的let的方法。

我想把它用作:

代码语言:javascript
复制
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的可空实例,如果不提供回退,则输出为可空字符串,如果提供非空回退,则为非空字符串。

下面是我写的原型:

代码语言:javascript
复制
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,但我不明白为什么这是非法的。我曾料到这一点(在显式语法中):

代码语言:javascript
复制
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 )

编辑:写完这篇文章后,我尝试了这两种实现路线。以下是我所写的:

代码语言:javascript
复制
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();
  }
}

出于某种原因,这是合法的:

代码语言:javascript
复制
var test = letNonNull(null, (it) => "whatever", () => null)

我原以为() => null回调将是编译器错误,因为O不能为null (根据我最初的观察:我不能返回null)。

似乎没有完全执行无效安全措施。

edit2:似乎只有在推断类型时才是合法的。例如:

代码语言:javascript
复制
letNonNull(null, (it) => "bogus", () => null); // legal
letNonNull<String, String>(null, (it) => "bogus", () => null); // illegal

我本来希望推断的类型是非空的.

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-05-18 09:10:09

您不能返回null,因为O可能被绑定到非空类型。类型变量不是“总是不可空的”,但它们始终是潜在的非空变量。返回类型为O的函数返回的内容必须对所有可能的O绑定有效,即使绑定到非空类型时也是如此。或者当它被绑定到Never时。这意味着唯一可能有效返回的类型是O本身,而null没有类型O。

如果希望始终能够返回null,则必须使let的返回类型成为O?。这使得它始终是可空的,即使O本身是不可空的。在这种情况下,我会将O限制为不可空,方法是给它一个extends Object的界。

另一种方法是,如果null是一个有效的返回值,则返回null,如果它不是,则抛出(因此不必返回任何内容,因为您没有任何要返回的东西),但是您的方法不适用于类型系统。试着改变

代码语言:javascript
复制
 if (null is O) {
    return null;
  }

至

代码语言:javascript
复制
  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类型总是不可空的。

代码语言:javascript
复制
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还需要对类型变量进行绑定:

代码语言:javascript
复制
O letNonNull<I extends Object, O>(
    I? value, O Function(I) cb, O Function() or) {
  if (value != null) {
    return cb(value);
  }
  return or();
}

原因

代码语言:javascript
复制
var test = letNonNull(null, (it) => "whatever", () => null)

是有效的,是它推断letNotNull<Object, String?>,和

代码语言:javascript
复制
letNonNull<String, String>(null, (it) => "bogus", () => null); // illegal

无效,因为O的类型不可为空。

类型系统不知道如何使用or函数参数,它只是检查提供的O的实际值是否是O Function()参数类型的一个适当的子类型。函数体的类型检查确保它只能用于结果可以接受的位置。这就是不允许在上面返回null的类型检查,因为该检查必须适用于O可以绑定的所有类型。

我会考虑将let操作定义为扩展方法,因为在查看回调之前,它首先对值进行类型推断。类似于:

代码语言:javascript
复制
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");
  }
}
票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67577995

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档