Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make it clear whether a built value can be a subclass of a class implementing Built #177

Closed
filiph opened this issue Jun 22, 2017 · 6 comments

Comments

@filiph
Copy link
Contributor

filiph commented Jun 22, 2017

I finally got to look at the code and it looks like built_value_generator works even when the class isn't directly implementing Built. Somehow, I was under the impression that all value classes must be basically pure (no superclass except Object). If I can use even just a little bit of inheritance to build several 'sibling' built values, that's amazingly helpful and definitely worth documenting. (Unless it's an anti-pattern, in which case it would be great to document why.)

In particular, I'd like to know if something like this will work (and if that's a good idea):

abstract class A implements Built {
  void _someHelperMethod() { 
    // ...
  }

  int get x;

  String get foo => "foo $x";
}

abstract class B extends A implements Built<B,BBuilder> {
  factory B([...]) = _$B;
  @override
  int get x;
  bool get isAwesome;
}

abstract class C extends A implements Built<C,CBuilder> {
  // ...
}

If this will work, I'd like to understand:

  • Do I need generics on A's implement clause? If so, what should I put there?
  • How do I tell built_value_generator that it shouldn't try to generate concrete class for A? I assume by putting it in a class that's not in the Phases, but is there a better / different approach?
  • Will all getters from the superclass be pulled into the generation process or do I need to @override them explicitly in B and C (as shown above)?
  • Can I make my B constructor any shorter using this approach?

(This is sort of a continuation of #91.)

@davidmorgan
Copy link
Collaborator

Right, the classes should only extend Object.

But, it should be possible to allow 1) pulling in fields via "implements", 2) pulling in code via "with", i.e. mixins.

Right now you can use "implements" to give classes common properties, but the code generation won't pick them up -- so you'll have to add the @overrides. Picking up properties from interfaces is a TODO, #22

Similarly you should be able to use mixins to share implementation, but the code generation will ignore it.

Does that work for what you want to do?

Thanks!

@filiph
Copy link
Contributor Author

filiph commented Jun 22, 2017

Ok, so the documentation could have this section:

Hierarchical model

built_value_generator currently doesn't fully support blah blah blah but you can work around this thusly:

abstract class Animal {
  int get legs;
}

abstract class Walking implements Animal {
  void walk() => print("I walk using my $legs legs.");
}

abstract class Cat extends Object
    with Walking
    implements Animal, Built<Cat, CatBuilder> {
  factory Cat(...) = $_Cat(...);
  
  @override
  int get legs;
}

abstract class Otyugh ... { ... }

Is that about correct? Will this work?

@davidmorgan
Copy link
Collaborator

Yes, exactly. Just tried it and it works. I'll add to the docs plus an example in the code. Thanks!

@filiph
Copy link
Contributor Author

filiph commented Jun 26, 2017

I just realized half of my built value classes actually extend from something else than Object. You have to remember to @override members (or you'll get errors on the _$Class implementation, which can be confusing at first), but otherwise it works great. I don't need to have a separate interface and mixin.

Any issue that I'm not foreseeing?

@davidmorgan
Copy link
Collaborator

Extending relies on the class you're extending having a default constructor and no fields; in practice you should be able to use it as a mixin instead.

You'll like this: https://github.com/google/built_value.dart/pull/186/files ... this adds automatically pulling in fields from interfaces, and makes it possible to create a non-instantiable Built to use as an interface. The example is probably the place to look: https://github.com/google/built_value.dart/pull/186/files#diff-8d9e54534132359b303e1bd4518fc99d

I'll leave this open though because I should add docs as well as an example.

@bradyt
Copy link

bradyt commented Mar 2, 2020

This is me trying to figure out which docs/example added clarification: https://github.com/google/built_value.dart/commits/master?after=5d912473ea5bd39a10834e40a498702f8c143738+419.

Would it be possible to add a note to this thread, about where I can find clarification on this topic?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants