如何在Dart中创建fromJson和toJson的通用实现?

huangapple go评论89阅读模式
英文:

How can I create a generic implementation of fromJson and toJson in Dart?

问题

我正在尝试实现一种允许使用泛型类型创建扩展类实例的继承模式。下面的代码似乎是一种有效的模式,但编译器抱怨说 T.fromJson() 未实现。具体而言,在VS Code中,它显示以下错误,带有红色下划线波浪线:

“方法 'fromJson' 未定义于类型 'Type'。请尝试更正名称为现有方法的名称,或定义名为 'fromJson' 的方法。”

我在重构过程中遇到了这个问题,但成功创建了一个我认为应该工作的半简化示例:

class TestClass {
  TestClass();

  factory TestClass.fromJson(Map<String, dynamic> json) => TestClass();
}

class ATest extends TestClass {
  ATest();

  @override
  factory ATest.fromJson(Map<String, dynamic> json) => ATest();
}

class BTest extends TestClass {
  BTest();

  @override
  factory BTest.fromJson(Map<String, dynamic> json) => BTest();
}

class CTest extends TestClass {
  CTest();

  @override
  factory CTest.fromJson(Map<String, dynamic> json) => CTest();
}

class FactoryController<T extends TestClass> {
  Future<List<T>> listNetworkObjects<T extends TestClass>({
    required List<String> filters,
  }) async {
    try {
      final data = await MockApi().mockApiCall<T>(filters);
      final List<T> allItems = List<T>.from(
        data.map((T model) => T.fromJson(model)),
      );

      if (allItems.isNotEmpty) {
        print('Received Items from the API.');
      }
      return allItems;
    } catch (getTestClassError, stackTrace) {
      print('Error getting TestClasses');
      print(stackTrace);
      return <T>[];
    }
  }
}

class MockApi {
  MockApi();

  dynamic mockApiCall<T extends TestClass>(List<String> filters) async {
    final dynamic data = {"test": "object"};
    return data;
  }
}

我期望方法 listNetworkObjects<T extends TestClass> 将为编译器提供基类以及所有实现它的类提供的 .fromJson() 方法的可见性。

data.map((T model) => T.fromJson(model)) 不同,我尝试了以下选项:

  • data.map((T model) => T().fromJson(model))
  • data.map((T model) => new T.fromJson(model))
  • data.map((T model) => new T().fromJson(model))
  • data.map((model as T) => T.fromJson(model))
  • data.map((T model) => T.new.fromJson(model))

每种方式都失败了,但有不同的失败原因。

我看到了其他类似的问题,例如有关抽象类构造函数的问题,但我的基类并不是抽象的。我还尝试使用 implements 而不是 extends,但:

listNetworkObjects<T implements TestClass>()

不是有效的方法或类规范。我觉得答案可能在这里,但我无法弄清楚如何正确实现它:
https://stackoverflow.com/questions/23112130/creating-an-instance-of-a-generic-type-in-dart

谢谢!

英文:

I am attempting to implement an inheritance pattern that allows for creating instances of an extended class using generic types. The below code appears to be a working pattern, yet the compiler complains that T.fromJson() is not implemented. Specifically, it gets red underlined squiggles in VS Code with the following error:

The method &#39;fromJson&#39; isn&#39;t defined for the type &#39;Type&#39;. Try correcting the name to the name of an existing method, or defining a method named &#39;fromJson&#39;.

I am encountering this during a refactor effort but was able to create a semi-simplified example of the pattern I think should work:

class TestClass{
  TestClass();

  factory TestClass.fromJson( Map&lt;String, dynamic&gt; json ) =&gt; TestClass();
}

class ATest extends TestClass {
  ATest();

  @override
  factory ATest.fromJson( Map&lt;String, dynamic&gt; json ) =&gt; ATest();
}

class BTest extends TestClass {
  BTest();

  @override
  factory BTest.fromJson( Map&lt;String, dynamic&gt; json ) =&gt; BTest();
}

class CTest extends TestClass {
  CTest();

  @override
  factory CTest.fromJson( Map&lt;String, dynamic&gt; json ) =&gt; CTest();
}

class FactoryController&lt;T extends TestClass&gt;{
  Future&lt;List&lt;T&gt;&gt; listNetworkObjects&lt;T extends TestClass&gt;({
    required  List&lt;String&gt;  filters,
  }) async {
    try {
      final data = await MockApi().mockApiCall&lt;T&gt;( filters );
      final List&lt;T&gt; allItems = List&lt;T&gt;.from(
        data.map( ( T model ) =&gt; T.fromJson( model ) )
      );

      if ( allItems.isNotEmpty ) {
        print( &#39;Received Items from the API.&#39; );
      }
      return allItems;
    }
    catch ( getTestClassError, stackTrace ) {
      print( &#39;Error getting TestClasses&#39; );
      print( stackTrace );
      return &lt;T&gt;[];
    }
  }
}

class MockApi{
  MockApi();

  dynamic mockApiCall&lt;T extends TestClass&gt;(
    List&lt;String&gt; filters
  ) async {
    final dynamic data = { &quot;test&quot;: &quot;object&quot; };
    return data;
  }
}

I would expect that the method listNetworkObjects<T extends TestClass> would provide visibility to the compiler that the base class and all implementations of it also provide an implementation of the .fromJson() method of the class.

Instead of data.map( ( T model ) =&gt; T.fromJson( model ) ) I have tried:

data.map( ( T model ) =&gt; T().fromJson( model ) )

data.map( ( T model ) =&gt; new T.fromJson( model ) )

data.map( ( T model ) =&gt; new T().fromJson( model ) )

data.map( ( model as T ) =&gt; T.fromJson( model ) )

data.map( ( T model ) =&gt; T.new.fromJson( model ) )

each of which fails for a different reason.

I see other similar questions about abstract class constructors etc. like
https://stackoverflow.com/questions/69943865/flutter-abstract-class-with-factory-methods
but my base class is not abstract. I also tried with using implements instead of extends but:

listNetworkObjects&lt;T implements TestClass&gt;()

is not a valid method or class specification. I feel like there could be an answer here but I can't figure out how to implement it properly:
https://stackoverflow.com/questions/23112130/creating-an-instance-of-a-generic-type-in-dart.

Thanks!

答案1

得分: 0

Dart存在一个限制,不能调用泛型类型的静态方法(包括构造函数和工厂方法)。

在某些情况下,我曾经使用以下类似的模式,缺点是它需要传入一个实例化的该类型对象:

abstract class TestClass {
  TestClass fromJson(Map<String, dynamic> json);
}

class Foo<T extends TestClass> {
  T someMethod(T proto) => proto.fromJson(...) as T;
}
英文:

It is a limitation of Dart that static methods of generic types cannot be called (including constructors and factories).

In some situations I've used a pattern like below, with the disadvantage that it requires an instantiated object of the type to be passed in:

abstract class TestClass{
  TestClass fromJson(Map&lt;String, dynamic&gt; json);
}

class Foo&lt;T extends TestClass&gt; {
  T someMethod(T proto) =&gt; proto.fromJson(...) as T;
}

huangapple
  • 本文由 发表于 2023年1月9日 10:53:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/75052781.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定