abstract classes and factory constructors

3,828 views
Skip to first unread message

Paul Brauner

unread,
Sep 18, 2012, 4:41:58 PM9/18/12
to General Dart Discussion
Hello, I'm migrating my code from interfaces and default classes to abstract classes and factory methods. The following program:

abstract class A {
  factory A() => new B();
}

class B extends A {
}

main() {
  new B();
}

yields

'file:///tmp/Test.dart': Error: line 5 pos 1: unresolved implicit call to super constructor 'A()'
class B extends A {

with the last VM (continuous build). dart2js complains that:

Test.dart:5:1: Error: cannot resolve constructor A for implicit super call
class B extends A {
^^^^^
Error: Compilation failed.

So if they both agree I guess I'm doing something wrong :) What am I doing wrong?

Paul

Gilad Bracha

unread,
Sep 18, 2012, 4:45:54 PM9/18/12
to General Dart Discussion
The default constructor for B calls the default *generative* constructor for A, which doesn't exist.  Put another way, if we allowed this, you'd have an infinite loop, as the B constructor would call the A factory that calls the B constructor ...

I would suggest that B implement A instead.



Paul

--
Consider asking HOWTO questions at Stack Overflow: http://stackoverflow.com/tags/dart
 
 



--
Cheers, Gilad

Paul Brauner

unread,
Sep 18, 2012, 4:52:35 PM9/18/12
to General Dart Discussion
On Tue, Sep 18, 2012 at 10:45 PM, Gilad Bracha <gbr...@google.com> wrote:
The default constructor for B calls the default *generative* constructor for A, which doesn't exist.  Put another way, if we allowed this, you'd have an infinite loop, as the B constructor would call the A factory that calls the B constructor ...


Ok, I had guessed something along these lines and had tried to add a generative constructor A() that does nothing but of course it clashes with the factory constructor.
 
I would suggest that B implement A instead.


So if A implemented some methods (say foo), the right way to do it is:

abstract class A {
  factory A() => new B();
}

class _A implements A {
  foo() => 42;
}

class B extends _A {
}

main() {
  new B();
}

Is that right? (Modulo bad choice of name of _A.)

Gilad Bracha

unread,
Sep 18, 2012, 4:56:41 PM9/18/12
to General Dart Discussion


On Tue, Sep 18, 2012 at 1:52 PM, Paul Brauner <po...@google.com> wrote:

So if A implemented some methods (say foo), the right way to do it is:

abstract class A {
  factory A() => new B();
}

class _A implements A {
  foo() => 42;
}

class B extends _A {
}

main() {
  new B();
}

Is that right? (Modulo bad choice of name of _A.)
 


That looks fine.  I assume you have other implementations that need to share the code in _A. Otherwise, you could have it all implemented in B.


--
Cheers, Gilad

Paul Brauner

unread,
Sep 18, 2012, 4:57:15 PM9/18/12
to General Dart Discussion
Yes I do. Thanks!

Bob Nystrom

unread,
Sep 18, 2012, 6:22:57 PM9/18/12
to mi...@dartlang.org
On Tue, Sep 18, 2012 at 1:52 PM, Paul Brauner <po...@google.com> wrote:



On Tue, Sep 18, 2012 at 10:45 PM, Gilad Bracha <gbr...@google.com> wrote:
The default constructor for B calls the default *generative* constructor for A, which doesn't exist.  Put another way, if we allowed this, you'd have an infinite loop, as the B constructor would call the A factory that calls the B constructor ...


Ok, I had guessed something along these lines and had tried to add a generative constructor A() that does nothing but of course it clashes with the factory constructor.
 
I would suggest that B implement A instead.


So if A implemented some methods (say foo), the right way to do it is:

abstract class A {
  factory A() => new B();
}

class _A implements A {
  foo() => 42;
}

class B extends _A {
}

main() {
  new B();
}

Is that right? (Modulo bad choice of name of _A.)

The other option, if you don't want to make an extra _A class, is to give A a constructor:

abstract class A {
  A();
  factory A() => new B();
  foo() => 42;
}

class B extends A {
}

main() {
  new B();
}

If you don't want people to call it, you can make it private:

abstract class A {
  A._();
  factory A() => new B();
  foo() => 42;
}

class B extends A {
  B() : super._();
}

main() {
  new B();
}

- bob

Paul Brauner

unread,
Sep 19, 2012, 5:18:47 AM9/19/12
to General Dart Discussion
On Wed, Sep 19, 2012 at 12:22 AM, Bob Nystrom <rnys...@google.com> wrote:


On Tue, Sep 18, 2012 at 1:52 PM, Paul Brauner <po...@google.com> wrote:



On Tue, Sep 18, 2012 at 10:45 PM, Gilad Bracha <gbr...@google.com> wrote:
The default constructor for B calls the default *generative* constructor for A, which doesn't exist.  Put another way, if we allowed this, you'd have an infinite loop, as the B constructor would call the A factory that calls the B constructor ...


Ok, I had guessed something along these lines and had tried to add a generative constructor A() that does nothing but of course it clashes with the factory constructor.
 
I would suggest that B implement A instead.


So if A implemented some methods (say foo), the right way to do it is:

abstract class A {
  factory A() => new B();
}

class _A implements A {
  foo() => 42;
}

class B extends _A {
}

main() {
  new B();
}

Is that right? (Modulo bad choice of name of _A.)

The other option, if you don't want to make an extra _A class, is to give A a constructor:

abstract class A {
  A();
  factory A() => new B();
  foo() => 42;
}

class B extends A {
}

main() {
  new B();
}


That's what I meant by " tried to add a generative constructor A() that does nothing but of course it clashes with the factory constructor". The problem is that then I get:

'file:///tmp/test.dart': Error: line 3 pos 15: field or method 'A.' already defined

      factory A() => new B();


Which makes sense anyway: how would you disambiguate new A()?
 
If you don't want people to call it, you can make it private:

abstract class A {
  A._();
  factory A() => new B();
  foo() => 42;
}

class B extends A {
  B() : super._();
}

main() {
  new B();
}


Ok, this works. However it feels more like a hack to me than the other solution in the sense that this may make little sense to an external reader of the code.

Thanks!

Sam McCall

unread,
Sep 19, 2012, 11:47:04 AM9/19/12
to mi...@dartlang.org
This makes sense, but it's pretty confusing at first glance to have a class (with non-static methods) that you cannot instantiate nor extend!

Lasse R.H. Nielsen

unread,
Sep 19, 2012, 11:54:48 AM9/19/12
to mi...@dartlang.org
True. It's one of those cases where you want all the features
independently, but when you combine them just right (or wrong) they
just fail to make sense.

There should probably be a warning if a class has non-static members
and no generative constructor.

You can get the same effect in Java by creating only a private
constructor on a class - it's uninstantiable and unextendable.

/L
--
Lasse R.H. Nielsen - l...@google.com
'Faith without judgement merely degrades the spirit divine'
Google Denmark ApS - Frederiksborggade 20B, 1 sal - 1360 København K -
Denmark - CVR nr. 28 86 69 84
Reply all
Reply to author
Forward
0 new messages