Skip to content

Support subclasses #36

@MarcelGarus

Description

@MarcelGarus

We should support serializing classes that extend other classes, like this:

class A {
  A(this.foo);

  final int foo;
}

class B {
  B({@required int foo, this.bar}) : super(foo);

  final int bar;
}

class C {
  C(int baz) : super(baz);
}

There are two parts to a solution for subclassing: Making it possible to write useful adapters for such classes and being able to generate such adapters automatically.

The first part – choosing an encoding

This part seems comparatively easy, but even this one is more difficult than it seems:

If we would simply agree that the superclass and subclass share a field id namespace, that would lead to difficult management problems: Imagine, A uses field id 0 and B uses field id 1. If A adds a new field id, it would need to choose id 2 (the next free one). Then, if B would add a new field, it would need to use field id 3 – already, things are pretty complicated (A is responsible for fields 0 and 2, B for 1 and 3) and that's only with two classes! Imagine having to find new field ids for new fields in a class with multiple inheritance levels below and above. That sounds like a pure nightmare to me.

Instead, the most promising solution I came up with so far would be to give each class its own field id namespace and have a block that is able to save multiple hierarchies – for example, if our class has two classes above it in the hierarchy, we could use a ListBlock containing three FieldsBlock, the first one containing the fields of the top-most class, the second one the fields of the class below that and the third one the fields of our actual class. Field ids remain used only in their own class, which makes for a much more hygienic solution.

Instead of saving them all in a ListBlock, we could also introduce a new block that saves a tuple – our own field ids and the parent class turned into a block. During decoding, we simply let the parent block take care of the decoding and then read useful information from the created object. That would certainly be the most loosely coupled solution – we would not depend on the inner workings of the parent class anymore, only on its public interface. If the parent class decides to change its inheritance hierarchy, choose to encode itself using a custom-built adapter or whatever, we don't care. We just use the parent class object as-is. Of course, this doesn't work for abstract parent classes (argh! This is so complicated!).
Another downside of this approach is that we create more objects than necessary. In the example given at the top, in the AdapterForB we would create an instance of A, just to get its foo property.

I'll have to think about the encoding a bit more. Until then, here are my thoughts so far on the second part of the problem:

The second part – auto-generating adapters

In order to support automatically creating superclasses, we need to understand the inner structure of the superclass. At least, we need to be able to parse its representation in the block layer to be able to turn it into a constructor.
We need to know which fields A has so that we can pass the right parameters into As constructor so that the super call uses the right parameters.
Sadly, there's no perfect way to do this. Up until now, we used constructors based on which of its parameters were initializing formals – that's the this.foo syntax in constructor parameters.
That doesn't work anymore with subclasses, because they accept values like String bar and then pass them to their superclass's constructor via super(bar).
That makes matching a bit more difficult. For example, the subclass could have the constructor

Bar({
  String first,
  String second,
  this.baz,
}) :
    super(first: second, second; first);

or even use calls like super(first: baz, second: baz). It's really difficult to decide what to do in such cases. It'll probably take some time until I figure this out…

Metadata

Metadata

Assignees

No one assigned

    Labels

    adapter generationThings that happen while generating adapters via pub run build_runner buildencodingThings that happen while encoding stuffopinion wantedExtra attention is needed

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions