I think I just hit a wall regarding method overloading and inheritance.

I think I just hit a wall regarding method overloading and inheritance.

I want to hide ancestor constructors. This is only possible if I only have one new constructor because that one will hide all overloads of the ancestor. But when I introduce a second one and overload these two I also get all of the ancestor constructors in. :(

Did I miss something?

Comments

  1. Offhand, hiding of constructors, or indeed any inherited method violates the (IIRC) Liskov substitution principle.  (Subtypes must be substitutable for their supertypes in all cases.  If subtypes allowed removal of methods [including constructors] then the promise that a subclass is substitutable for a superclass reference is broken and badness result.)    

    Offhand (again) with my pragmatic hat on, I think the most reasonable-but-perhaps-slightly-smelly thing you can do in practice is to replace the inherited constructors with new ones in the subclasses (so you still maintain the substitutability, at least at a compiler/interface level), and raise an exception in the constructor to alert the user of your code that this constructor is not meant to be used.  I seem to remember we did this many years ago when we had to/wanted to derive from some VCL classes which was suitable for our purposes but happened to have some constructors which didn't really fit in with what we were doing.  So we basically just overrode/replaced them and blocked their use. It worked fine in practice. (IIRC, it's been a while.)

    You can also of course (perhaps) write a new class that happens to wrap the old one and has the same interface minus what would've been an inherited constructor.  But that would mean you won't have a common ancestor and won't be assignment compatible directly.  Which means you'd have to use interfaces if you want to provide some compatibility between objects.   I'm not sure if that would fit your use case... (suspect not.)  And it seems like a lot of needless duplication/hassle.  Somehow however I suspect all these thoughts must've already occurred to you?  Apologies if I'm not really helping... just thoughts off the top of my head! :)

    ReplyDelete
  2. Fair enough.  I don't agree with the perspective in that SO though obviously [at least for Delphi, due to it supporting virtual constructors].   (To my mind constructors aren't all that special.  After all, in the end they do form part of the contract of the class -- they effectively say: "You can create instances using this/these method(s), it has this signature."  They are just methods that happen to return instances of a class. And in Delphi they are written almost as ordinary methods, and they support overriding, which to my mind means LSP should apply.  Anyway, sorry, I'll stop now.  I don't mean to be argumentative about this. :)  )

    ReplyDelete
  3. I can't get it to hide the ancestor in any way, as long as the ancestor is overloaded.

    Still, sounds like a very, very odd thing to want to do. I guess you don't have control over the ancestor then?

    ReplyDelete
  4. I just guess:
    1/ when you only have one constructor and overloading parent constructorS WITHOUT adding overload keyword followed by child constructor (sure you won't add keyword, because you just have one). At this time, all parent constructors will be hidden. this behavior just like c++ (sorry, i am more familiar with bc++).
    2/ when you introduce second constructor, this implies you need to add overload keyword for each child constructor.  At this time, the behavior very different, because compiler think since parent constructors with overload key words as same as child, it will expose every overloading constructor.

    So if what i guess is right, you did not missing something. It's natural.

    ReplyDelete
  5. You can place a class between the two classes that hides all the overloaded base constructors.

    type
      TMultiCtors = class(TObject)
      public
        constructor Create; overload;
        constructor Create(A: Integer); overload;
        constructor Create(A: Integer; B: string); overload;
      end;

      THideAllCtors = class(TMultiCtors)
      strict protected
        constructor Create;
      end;

      TMyImpl = class(THideAllCtors)
      private
        constructor Create(C: array of Integer); overload;
        constructor Create(X: Double); overload;
      end;

    ReplyDelete
  6. Andreas Hausladen Unfortunately I then cannot call one of the ctors of TMultiCtors from the ctor of TMyImpl anymore which is currently the case.

    ReplyDelete
  7. Stefan Glienke 
    constructor TMyImpl.Create(X: Double);
    begin
       TMultiCtors(Self).Create(10, 'Str');
    end;
    ?

    ReplyDelete
  8. Won't this hide the origin class constructors?

      TBaseClass = class
      public
        constructor Create(A: String); overload; virtual;
        constructor Create(A: Integer); overload; virtual;
      end;

      TSecondClass = class(TBaseClass)
        constructor Create; reintroduce;
      end;

    ReplyDelete
  9. Lars Fosdal You only need reintroduce if something was virtual. And what if I want more than one constructor in TSecondClass? What I would need is an overload that only overloads the oconstructors in the same class and not already existing ones.

    ReplyDelete

Post a Comment