views:

159

answers:

6
+2  Q: 

Java inheritance

So I've been trying to find the proper way to get what should be pretty straightforward inheritance to function (the way I want ;)) and I'm failing miserably. Consider this:


class Parent
{
  public string name = "Parent";

  public Parent() {};
  public doStuff()
  {
     System.out.println(name);
  }
}

class Child extends Parent
{
  public string name = "Child";
  public Child()
  {
    doStuff();
  }
}

Please ignore any silly syntax errors as this is more of a conceptual question. I understand that Java doesn't do what I'd expect in this case as for some reason member variables aren't overridden the same way methods are but my question is what is the proper way to achieve the behavior I'd expect? I'd like my base class to contain some functions that operate on member variables provided/defined by a given subclass. I'd think get/sets could work, but then it destroys my ability to just call super() and have my base class do the construction needed making my subclasses contain a bunch of construction instead of isolating it to the base class.

Any constructive suggestions are welcome.

+3  A: 

Do you really need two different name variables for a single instance? Do you genuinely have the idea of an object having a "name-when-viewed-as-Parent" and a "name-when-viewed-as-Child"? If not, just have a single variable in the Parent class.

How would the Parent class know which variables are going to be provided by the child? If every subclass is going to have the same set of variables, why aren't they in the parent class?

If you could give a more concrete example of the problem you're trying to solve, it would make it easier to give a concrete answer.

Jon Skeet
Jon, thanks for the dialog. The reason I don't want them declared in the Parent is that each subclass will have different values for whichever member. Consider a different array of values for each subclass with a function in the baseclass that will operate on that array. I'd have no problem with the array declaration occuring in the Parent, there just doesn't seem to be a clean way of allowing the subclass to 'override' (or maybe redefine is a better choice of words) said array.
@user483315: Each instance of each subclass will already have different values. Do you mean that each variable would be a different *type* in each subclass?
Jon Skeet
No, I'm not changing types, but the issue is I'd like a function in the base class to be able to operate on an array being defined in the subclass. It seems the accepted practice is to just pump that data into the base class by hand in the subclass constructor, it just strikes me as gross that for some reason member variables got totally left out of the inheritance/overriding chain and instead it's just accepted as these 'shadow' variables.
+1  A: 

The subclass variable name is shadowing the super classes variable of the same name. So when Child declares and assigns name it's another separate variable.

Instead, consider name to be a property of the parent inherited by the child and pass name as an argument to the Parent constructor. Then you would invoke super("Child") from the child constructor.

Also, as a public property, it should be final. Otherwise expose it via getters and setters.

Software Monkey
+6  A: 

You can always replace a variable with getter, if you want such kind of 'variable-inheritance'. Not sure if 'variable-inheritance' ((c)) is a good design pattern in general, though.

class Parent {
    public void doStuff() {
        System.out.println(getName());
    }

    public String getName() {
        return "Parent";
    }
}

class Child extends Parent {
    public Child() {
        doStuff();
    }

    public String getName() {
        return "Child";
    }
}

Or you could have protected constructor in Parent class, taking the name to use.

edit
I wouldn't call it a 'hack'. And version with passing parameter into parent constructor can be pretty elegant too.

class Parent {
    private final String name;

    // don't let use to invoke this constructor, only for child classes
    protected Parent(String name) {
        this.name = name;
    }

    // public constructor for users
    public Parent() {
        this("Parent");
    }

    public void doStuff() {
        System.out.println(name);
    }
}

class Child extends Parent {
    public Child() {
        super("Child");
        doStuff();
    }
}

do you have any idea why Java seems to have abandoned the concept of inheriting member variables?
Java has abandoned a lot of C++ concepts (starting from infamous multiple inheritance), because they didn't add that much value to the language, but increased its complexity.
Although I don't have anything against variable-inheritance, I'm not suffering without this concept either. And personally, I like the language simple and clean.

Nikita Rybak
Agree, if you want an overridable field, just use a getter
Juan Mendes
Please see my reply to Stephen C, I understand the hack to make it work, I was just hoping someone had come up with a more...elegant solution than having to marshall the data back into the base class manually. As a follow on though, do you have any idea why Java seems to have abandoned the concept of inheriting member variables? It seems just wrong to me, but what do I know :P.
@Nikita the part that strikes me odd though is that leaving it out actually causes inelegance, especially in this situation as I'd like to use the base class constructor to actually construct some common things between my subclasses but I can't as I have to 'set' the base class up in my subclass constructor and it seems the second I do anything in the subclass constructor I lose the ability to execute the base class's constructor. The only other thing I can think of is to resort to an .init() style call after that, but that is still a little gross.
@user483315 I don't exactly understand your point, but you loose nothing by calling base constructor explicitly. Also note, that if you don't call base constructor explicitly, it's still called under the hood, as if you had _super();_ call as first line in your child constructor. And it doesn't prevent you from having other initialization code in your parent class.
Nikita Rybak
@user483315 If you clarify what you wanna do, maybe we could suggest some nice approach for that.
Nikita Rybak
+1  A: 

You are correct in that you can't override members of the class. You can achieve this by making name protected and setting it in the constructor.

class Parent {   
    protected string name;

    public Parent() { name = "parent"};   
    public doStuff() {
        System.out.println(name);   
    }
}

class Child extends Parent {   
    public Child() {
        name = "child";
        doStuff();
    }
}
shoebox639
A: 

What you are trying to do won't work in Java. In Java, attributes declared in a parent class are not overridden in a child class.

If you want polymorphic behavior, then you need to declare the attributes private, and declare corresponding getter methods in the parent class that you can override in the child class.

Alternatively:

class Child extends Parent {
    public Child() {
        name = "Child";
        doStuff();
    }
}

though @shoebox639's answer does this in a more elegant way that allows you to declare the attribute as final. (Mutable public attributes are not a good idea ...)

Stephen C
Sure, the only part of that solution that sucks (imo) is that using this idiom essentially forces my base class's constructor into uselessness since I can't construct anything until these values get pumped in and once I pump the values in, I can't call the constructor any longer. I've resigned myself to adopting this, I just find it slightly repulsive :).
A: 

Using inheritance if you really want to have each child contain the name of it's respective class separately from the Parent, then do it like this:

class Parent
{
    public final string name;

    public Parent(String _name) {
        name = _name;
    }
    public doStuff()
    {
        System.out.println(name);
    }
}

class Child extends Parent
{      
    public Child()
  {
    super("Child");
    doStuff();
  }
}

Call the Parent's constructor and have the child pass the name you want.

wheaties