Dart - 实现一个 class 方法,参数已实现 class

Dart - implementing a class method with argument as implemented class

我正在开发外部库。假设我有不同的记录器实现 class,我创建抽象 class ILogger 然后那些 classes 可以实现。

我也有各种日志对象的实现,我想遵循 ILog 抽象 class 所以我也公开它。

问题是当我的 ILogger 使用 ILog 作为其方法之一的参数时。我会假设我实现的任何记录器 classes(实现 ILogger)将接受任何 log classes(实现 ILog)的参数。

请参阅下面我的约束示例:

abstract class ILog {
  const LogImpl({required this.id});
  final String id;
}

class Log implements ILog {
  const Log({required this.id});
  
  final String id;
}

abstract class ILogger {
  void writeLog({
    required LogImpl log,
    required bool persist,
  });
}

class Logger implements ILogger {
  void writeLog({
    required Log log,
    required bool persist,
  }) {
    print('writing $log with persistence? $persist');
  }
}

void main() {
  final logger = Logger();
  final log = Log(id: 'abcd-1234');
  
  logger.writeLog(log: log, persist: true)
}

为此我得到错误:

Error: The parameter 'log' of the method 'Logger.writeLog' has type 'Log', which does not match the corresponding type, 'ILog', in the overridden method, 'ILogger.writeLog'.

有没有一种不用应用泛型就能解决这个问题的方法? 我的日志对象 ILog 需要抽象 class 而不是扩展的常规 class 的原因是技术性的。我的一个序列化库使用源代码注释,这意味着该注释不能成为库的一部分(因为不同应用程序的注释可能不同)。

程序未编译,因为它不健全。 Dart 和一般的面向对象编程都是基于 子类型可替换性 。如果您的代码适用于类型的实例,它也适用于子类型的实例 - 子类型可以替代超类型。

LoggerwriteLog 方法 不是 ILoggerwriteLog 方法的有效覆盖。后者接受一个 ILog 作为参数,并且为了能够替代它的子类型,它也需要接受一个 ILog。但是,它只接受 Log,这是一个子类型,而不是 any ILog.

的实现

另一种方法是承认您正在编写的内容不可靠,并告诉编译器无论如何都要接受它:

class Logger implements ILogger {
  void writeLog({
    required covariant Log log,
    required bool persist,
  }) {
    print('writing $log with persistence? $persist');
  }
}

这里的covariant告诉编译器,你知道Log不满足ILog的超类参数类型,但是没关系。你知道你在做什么,没有人会用不正确的东西调用这个函数 Log。 (如果他们这样做了,它会抛出一个错误)。

我可能会推荐的另一种选择是在它使用的 Log 上参数化您的 类:

abstract class ILogger<L extends ILog> {
  void writeLog({
    required L log,
    required bool persist,
  });
}

class Logger implements ILogger<Log> {
  void writeLog({
    required Log log,
    required bool persist,
  }) {
    print('writing $log with persistence? $persist');
  }
}

因此,Logger不必替代所有 ILoggers,只需替代ILogger<Log>,它可以做得很好。 (好吧,正如固有的不健全的协变泛型所允许的那样健全,但程序将 编译 ,如果你传递的不是 Log 实例的东西,则抛出。)

在这两种情况下,编译器都会知道 Logger 的参数必须是 Log。在这两种情况下,您都可以通过将 Logger 转换为超类型 ILogger/ILogger<ILog>,然后将 ILog 传递给 writeLog 来欺骗程序,但是绕过类型系统至少需要一点努力。