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

Constructor inheritance #9468

Open
DartBot opened this issue Mar 27, 2013 · 10 comments
Open

Constructor inheritance #9468

DartBot opened this issue Mar 27, 2013 · 10 comments
Labels
area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). type-enhancement A request for a change that isn't a bug

Comments

@DartBot
Copy link

DartBot commented Mar 27, 2013

This issue was originally filed by efesak...@gmail.com


Hi,

i am coming from PHP world and i am curious why developers choose the way not adding constructor (with arg) to inheritance. From my view it violates DRY principle by repeating code. I did little research - PHP, Ruby, Python inherits constructor. Java, C#, C++ not. C++0x has new feature - method explicitly defining constructor inherition.

Is there any reason or advantage for programmer not having constructor inherited?

(i am not experienced dart nor java programmer, but this makes me no sense in modern language)

@lrhn
Copy link
Member

lrhn commented Mar 28, 2013

Removed Type-Defect label.
Added Type-Enhancement, Area-Language, Triaged labels.

@lrhn
Copy link
Member

lrhn commented Mar 28, 2013

This could work: If you extend a class and specify no constructors, you currently get a default constructor "myClass(): super();".
You could instead get default constructors forwarding to all of the superclass' generative constructors. The moment you declare your own constructor, you inherit nothing, as usual.
This matches what is currently specified for mixins anyway.
If you do

class C {
  C.foo();
}
class D extends C {} // no constructor inherited, and it's an error.
class E {}
class F = C with E;  // inherited constructor!

It would be consistent to make the constructors inherit everywhere. It should not be a breaking change (but maybe an inconvenient one), since any class that currently gets a default constructor added will have a superclass with a unnamed zero-argument constructor, and so it will still get the same constructor. It might inherit more constructors too, which is the inconvenient part.

@DartBot
Copy link
Author

DartBot commented Jul 4, 2013

This comment was originally written by ras...@mindplay.dk


For the record, I also have a long history with PHP, as well as many other languages.

From my perspective, explicit constructors do not violate the DRY principle, because you're not simply repeating yourself.

The constructor in a base-class defines the needs of the base-class. A similar constructor in a sub-class defines the needs of the sub-class, which aren't automatically the same as those of the base-class - to assume so, is to assume that the author of the base-class can predict the future, which may be the case in a closed system, but in the case of open systems (such as frameworks) you can't make those assumptions.

I wrote a bit of a dissertation on the same subject on stackoverflow:

http://stackoverflow.com/questions/15665354/why-dart-does-not-inherit-constructor/17472936#­17472936

@gbracha
Copy link
Contributor

gbracha commented Aug 25, 2014

Much could be said about the disadvantages of constructors, and in fact I have:

http://gbracha.blogspot.com/2007/06/constructors-considered-harmful.html

Dart constructors solve some but not all of the issues.

Constructors must deal with the task of initializing the instance, and thus are specialized in subclasses. In general, it is not valid to inherit a constructor, as it will not properly initialize the subclass. So you should not have constructor inheritance by default. In languages where class methods act as constructors (like Ruby) you get inheritance by default, and it is often a problem because you can instantiate a class and not initialize it probably.

 In those cases where the a subclass requires no further initialization, and no change to the incoming parameters, it would be convenient to not have to introduce special constructors. In some cases, this actually works (if you only use default constructors). Those situations where you really have to define fairly pointless constructors to forward the constructor calls to the superclass are the minority. For those cases, one could imagine a sugar saying you want to inherit the constructors from the superclass. You'd have to state that, and it would only be worthwhile if it were substantially shorter than the new constructors.

It seems an exceeding amount of sugar to me, and I would not expect it any time soon.


Set owner to @gbracha.
Added Accepted label.

@DartBot DartBot added Type-Enhancement area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). labels Aug 25, 2014
@kevmoo kevmoo added P2 A bug or feature request we're likely to work on type-enhancement A request for a change that isn't a bug and removed accepted labels Feb 29, 2016
@munificent munificent changed the title Feature request: Inherit constructor Constructor inheritance Dec 16, 2016
@crtl
Copy link

crtl commented Aug 31, 2020

@gbracha

Constructors must deal with the task of initializing the instance, and thus are specialized in subclasses. In general, it is not valid to inherit a constructor, as it will not properly initialize the subclass.

The parent does not know about the child but wise versa. Therefore the child is also responsible for initializing itself and the parent correctly. The parent constructor works in itself and describes its parameters in its signature. If the child fails to pass the required arguments its the childs fault. Constructors are specialized in nothing but the class that they belong to. What is the opposite of a "specialized" constructor? A "generalized" constructor? This doesnt make sense.

Those situations where you really have to define fairly pointless constructors to forward the constructor calls to the superclass are the minority.

No they are not. Basically you have to to this in every class you extend which accepts constructor arguments. I had multiple occurences where I did not want to use inhertance because I have to copy all the parameter lists for constructors just to override a method.

For those cases, one could imagine a sugar saying you want to inherit the constructors from the superclass. You'd have to state that, and it would only be worthwhile if it were substantially shorter than the new constructors.

Why do I dont want to inherit the constructor?
The constructor is an required integral piece of logic tied to the parent class. Just extending the parent doesnt remove the need for it and if it is not needed anymore you override it.

Here is a usecase:

class ListBloc<T> {
  
  final ListRepositoryInterface repository;

  compareItems(T element) => element == item;

  ListBloc(@required this.repository);
}

class EntityListBloc extends ListBloc<Entity> {

  /// Here if have to copy/paste the complete constructor for no reason because if not I cant pass [repository]
  EntityListBloc(@required ListRepositoryInterface repository): super(repository);

  compareItem(Entity element) => this.item.id == element.id;
}

I also have PHP experience so heres an working example:

class Superclass {
  protected $prop;

  public function __construct($prop) {
    $this->prop = $prop;
  }
  
  public function printProp() {
    echo $prop;
  }
}

/**
 * Override method, constructor is inherited
 */
class FirstChild extends Superclass {
  printProp() {
    echo "Hello: $prop";
  }
}

/**
 * Override constructor
 */
class SecondChild extends Superclass {
  public function __construct($prop) {
    // Do stuff
   parent::__construct($prop);
  }
}

$super = new Superclass("World");
$child1 = new FirstChild("World");
$child2 = new SecondChild("World");

$super->printProp(); // World
$child1->printProp(); // Hello: World
$child2->printProp(); // World

I cannot really understand the disadvantages of constructor inheritance.
Also it does not add any BC issues.

EDIT:
Im currently working on an event bus and this example show perfectly how constructor inheritance can improve code quality.
Given an Entity which can be created, viewed and updated we need diffrent types of events.
The generic type system allows us to use Type as an event identifier.
Now when we create our events we have to duplicate all constructors:

abstract class EntityEvent {
  final Entity entity;

  EntityEvent(this.entity);
}

class EntityCreatedEvent extends EntityEvent {
  EntityCreatedEvent(Entity entity): super(entity);
}

class EntityViewedEvent extends EntityEvent {
  EntityViewedEvent(Entity entity): super(entity);
}

class EntityUpdatedEvent extends EntityEvent {
  EntityUpdatedEvent(Entity entity): super(entity);
}

This could be written like the following:

abstract class EntityEvent {
  final Entity entity;

  EntityEvent(this.entity);
}

class EntityCreatedEvent extends EntityEvent { }

class EntityViewedEvent extends EntityEvent { }

class EntityUpdatedEvent extends EntityEvent { }

@lrhn lrhn removed the P2 A bug or feature request we're likely to work on label Aug 31, 2020
@idkq
Copy link

idkq commented Mar 11, 2021

I have to copy all the parameter lists for constructors just to override a method.

Agree. This is utterly insane. There should be a way to override constructors (at least) and inherit the list of parameters. If you are extending a class, by default it implies that you will be reusing those same params.

@esDotDev
Copy link

esDotDev commented Feb 10, 2022

Just as an example, working in a tween lib right now, and every single sub-class needs this boilerplate:

class Move extends Tween<Offset> {
   const Move({required Offset from, required Offset to, Curve? curve})
        : super(from: from, to: to, curve: curve);

Which ends up being about 50% of the body of the class.

Where base class should really be handling it already for most tweens w/

abstract class Tween<T> {
  const Tween({required this.from, required this.to, this.curve});
  final T from;
  final T to;
  final Curve? curve;

It would be really nice if we could optionally change some defaults, but still inherit the overall signature somehow, as that is a common use case.

Another issue with duplicating params, is you often have to duplicate the default values. This is definitely not dry.

A good use case for this is MinSizeRow/MinSizeCol in flutter. It would be a very useful class, it only changes 1 default param value. but creating the class is like 20 lines of boilerplate, and (worse) we have to duplicate the other default params from Row, when we really just want to inherit them.

Instead of this:

class MinSizeRow extends Row {
  MinSizeRow({
    Key? key,
    MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
    MainAxisSize mainAxisSize = MainAxisSize.min, //           <!-----   only change we want to make
    CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
    TextDirection? textDirection,
    VerticalDirection verticalDirection = VerticalDirection.down,
    TextBaseline? textBaseline,
    List<Widget> children = const <Widget>[],
  }) : super(
          children: children,
          key: key,
          mainAxisAlignment: mainAxisAlignment,
          mainAxisSize: mainAxisSize,
          crossAxisAlignment: crossAxisAlignment,
          textDirection: textDirection,
          verticalDirection: verticalDirection,
          textBaseline: textBaseline,
        );
}

Would be really nice if we could instead write something closer to:

class MinSizeRow extends Row {
  MinSizeRow(inherited) : super(this.mainAxisSize = MainAxisSize.min);
}

@eernstg
Copy link
Member

eernstg commented Feb 10, 2022

There is a new language feature coming up (probably 2.17, no promises) known as super parameters, and it will allow you to do it in the following way:

class MinSizeRow extends Row {
  MinSizeRow({
    super.key,
    super.mainAxisAlignment,
    super.mainAxisSize = MainAxisSize.min, // Default value can be included, useful if different from super.
    super.crossAxisAlignment,
    super.textDirection,
    super.verticalDirection,
    super.textBaseline,
    super.children,
  });
}

It isn't entirely as concise as inherited, but it does remove a lot of boilerplate.

@esDotDev
Copy link

Nice, ty!

@crtl
Copy link

crtl commented Jun 7, 2022

There is a new language feature coming up (probably 2.17, no promises) known as super parameters, and it will allow you to do it in the following way:

class MinSizeRow extends Row {
  MinSizeRow({
    super.key,
    super.mainAxisAlignment,
    super.mainAxisSize = MainAxisSize.min, // Default value can be included, useful if different from super.
    super.crossAxisAlignment,
    super.textDirection,
    super.verticalDirection,
    super.textBaseline,
    super.children,
  });
}

It isn't entirely as concise as inherited, but it does remove a lot of boilerplate.

Seems like a neat workaround but still requires to copy the constructor.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). type-enhancement A request for a change that isn't a bug
Projects
None yet
Development

No branches or pull requests

8 participants