简体   繁体   中英

How to implement an abstract class of generic type?

I'm woriking on a Java project regarding binary search trees. We were asked to implenet and AVL tree, but also advised to keep good abstraction in order to implement other types of trees (such as red-black tree). I decided to use an abstract BsNode class (Bs = binary search) and an abstract BsTree class, and implement them with AvlTree and AvlNode in the following way:

public abstract class BsNode<T extends BsNode> {
    T parent
    T left
    T right
    ...
}

public abstract class BsTree<T extends BsNode> {
    T root
    ...
}

public class AvlNode extends BsNode<AvlNode> {
    int balance;
    ...
}

public class AvlTree<AvlNode> {
    private void rotate(int direction);
    ...
}

This causes two problems: First, it doesn't make sence to me that BsNode needs to receive it's inheritor type. Second, this opens the possibilty for such a thing to happen:

public class RedBlackNode extends BsNode<AvlNode> {
    ...
}

or

RedBlackNode myRoot = new RedBlackNode<AvlNode>();

and these should not be allowed. How can I enforce the usage of the inheriting class in BsNode (meaning to keep the parent and children pointers to the type that is implementing the class) instead of passing the T generic variable?

As mentioned in comments this is not possible to enforce at compilation time. There is a trick to force it to fail at run-time.

What you do is to request implementing classes to pass their own type-parameter class to the the abstract constructor with an argument typed with the type-parameter class.

The parent class constructor verifies that indeed this's class is the same class as the class that corresponds to the type-parameter's.

abstract class BsNode<T extends BsNode<T>> {

   protected BsNode(final Class<T> clazz) {
       if (!this.getClass().equals(clazz)) 
          throw new IllegalArgumentException("invalid mixture of node types");
   } 
}

class AvlNode extends BsNode<AvlNode> {

   public AvlNode() {
        super(AvlNode.class); // works!!!
   }

}

class RBNode extends BsNode<AvlNode> {

   public RBNode() {
       // two possible super calls:
       super(AvlNode.class); // fails at run-time.
       // or
       super(RBNode.class); // fail at compilation-time.
   }
}

If you have a formal build process as part of your software that contains testing, it might make more sense to leave the constrain check out of the production code (saving the computation time) and instead make it part of your unit-tests.

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