简体   繁体   中英

Modelling selective inheritance trait in Java?

I'm not sure if I'm asking the right question. Please correct it if I'm wrong.

I have 3 classes that models inheritance where some subclasses should not inherit a parent attribute.

Let's take for example the below classes:

abstract public class Root{
    // Has some attributes that must be inherited by it's sub classes
    List<Child> children;
}

abstract public class Child extends Root {
    // Has the root's attributes, plus additional attributes pertaining to the child class, including the position.
    private int position;
}

abstract public class Leaf extends Child {
    // this class inherits all attributes of child class but it does not have any children
    // How would I exclude the children attribute here?
}

I was thinking about using an interface but then it doesn't allow me to hold mutable data for the classes like an abstract class can. For example, I want to hold specific variables pertaining to the Child and Leaf class, such as the position of the child in the list of its parent.

I can't do this either:

abstract public class Leaf {
    // This child does not have a parent, but then again, it's not considered a child either because it comes before the Root who has children.
}

abstract public class Root extends Leaf {
    List<Child> children;
}

abstract public class Child extends Root {
    private int position;
}

Again, how would I model this?

Your question is confusing as it uses parent/children relationships in terms of reality and programming without specifying which you mean. However, I think I have a general understanding as to what you are asking.

I believe what you are looking for is the Composite Pattern
(See: https://en.wikipedia.org/wiki/Composite_pattern )

In the following implementation, Child and Childless have become part of the Composite pattern and has been abstracted an extra layer from Root by adding a Person class.

So you would have

abstract public class Root {
    //attributes that everything has
}

abstract public class Person extends Root {
    protected int position; //All children have parents
    //other attributes
}

abstract public class Child extends Person {
    protected List<Person> children; //But only Child has more children
    //other attributes
}

abstract public class Childless extends Person{
    //attributes
}

Person is the "component" and now has the position field because every child has a position in the list of the parent.

Child is the "composite" and now has the children field because every child that is not barren can have more children.

Childless is the "leaf" because it can no longer have more children.

Also, keep in mind that fields that are private cannot be accessed by subclasses, so use protected instead.

A simple flip of not having Leaf be a subclass of Child , and moving the field, works

abstract public class Node {
    private int position; //Every Node can have a position
}

public class InnerNode extends Node { //Renamed for clarity
    List<Node> children; //Children can be InnerNodes or Leafs 
}

abstract public class LeafNode extends Node {
    //As this is no longer an extension of InnerNode, it won't have the children field.
}

This model makes more sense IMO. Every LeafNode and InnerNode are considered Nodes, but only InnerNodes have children.

If you want, you could even add an abstract getter to Node to return children, and have LeafNode return null or an empty list:

abstract public class Node {
    private int position; //Every Node can have a position

    public abstract List<Node> getChildren();
}

public class InnerNode extends Node { //Renamed for clarity
    List<Node> children; //Children can be InnerNodes or Leafs 

    public List<Node> getChildren(){
        return new ArrayList<>(children);
    }
}

abstract public class LeafNode extends Node {
    //As this is no longer an extension of InnerNode, it won't have the children field.

    public List<Node> getChildren() {
        return new ArrayList<>(); //Empty
    }
}

This accomplishes roughly what you set out to do - only some Nodes (InnerNodes) will actually have children, but you can attempt to access the children of an arbitrary Node instance without casting. The following is now possible:

Node n = ...
List<Node> children = n.getChildren();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM