Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this subclass' parent method call not polymorphic?

I've been dabbling in Dlang recently as C++ just wasn't quite sitting right with me after having used Python for so long. While dabbling, I came across what I thought would be a very simple exercise in polymorphism. I suppose how you would expect something to work and what it actually does are two entirely different things for reasons an end user probably can't comprehend. That being said, here is the source code of my "sandbox.D":

import std.stdio;

class Animal {
    string voice = "--silence--";
    void speak() {
        writeln(this.voice);
    }
}

class Dog : Animal {
    string voice = "Whoof!";
}

int main() {
    auto a = new Animal();
    auto d = new Dog();

    writeln(a.voice); // Prints "--silence--"
    writeln(d.voice); // Prints "Whoof!"

    a.speak(); // Prints "--silence--"
    d.speak(); // Prints "--silence--" NOT "Whoof!"

    return 0;
}

I guess my issue is why the "this" keyword just doesn't seem to be functioning how you would expect it to in the C++ successor language.

like image 680
Chris Shelton Avatar asked Jul 15 '15 16:07

Chris Shelton


2 Answers

Methods are polymorphic, variables aren't. So instead of making the voice a variable, you want to override speak in the child.

Also, the auto return type doesn't work with polymorphism, you need to actually specify the types. (The reason is that auto return makes a function template in the compiler, which in theory could have multiple overridable slots in the function table, so it just doesn't try to put it in.)

So try this out:

import std.stdio;

class Animal {
  void speak() { // changed to void instead of auto
    writeln("--silence--");
  }
}

class Dog : Animal {
  override void speak() { // the override tells it to override the base method
    writeln("woof");
  }
}

int main() {
    auto d = new Dog();
    d.speak();
    return 0;
}

If you have a lot of shared functionality and want to reuse one function with slight changes in child classes, you might make a method instead of a variable that just returns something.

Like string voice() { return "woof"; }, then it can be overridden in children.

like image 62
Adam D. Ruppe Avatar answered Nov 25 '22 19:11

Adam D. Ruppe


Another way is to use template this parameter:

import std.stdio;

class Animal {
    string voice;

    void speak(this C)() {
        writeln((cast(C)this).voice);
    }
}

class Dog : Animal {
    string voice = "Whoof!";
}

int main() {
    auto a = new Animal();
    auto d = new Dog();

    a.speak(); // Prints ""
    d.speak(); // Prints "Whoof!"

    return 0;
}

Or when you do not need to have voice as a member:

import std.stdio;

class Animal {
    static immutable voice = "";

    void speak(this C)() {
        writeln(C.voice);
    }
}

class Dog : Animal {
    static immutable voice = "Whoof!";
}

int main() {
    auto a = new Animal();
    auto d = new Dog();

    a.speak(); // Prints ""
    d.speak(); // Prints "Whoof!"

    return 0;
}
like image 25
Kozzi11 Avatar answered Nov 25 '22 18:11

Kozzi11