Using generics
You've mentioned that you'd like to avoid typecasting and use generics instead. This is tricky, because a list shouldn't reveal it's implementation details, and making LockDList generic would do that; we can't just do
public class LockDList<T extends DListNode> extends DList {}
because now the user not only can know the internal mechanics, they must know. If we want to use generics, it has to be done internally with a factory. For example,
private class NodeFactory<T extends DListNode> {
private final Constructor<? extends T> ctor;
NodeFactory(Class<? extends T> impl) throws NoSuchMethodException {
this.ctor = impl.getConstructor();
}
protected T getNewNode(Object item, DListNode prev, DListNode next) throws Exception {
return ctor.newInstance(item, prev, next);
}
}
This is a little tricky. We have to have a Constructor object because Java uses type erasure, meaning that at runtime all Java knows about T is that it is some subclass of DListNode. This means that we also need all of this messy error handling in order to compile. Then in the body of our class, we can do something like this.
private NodeFactory<LockDListNode> factory;
{
try {
factory = new NodeFactory(Class.forName("LockDListNode"));
} catch (Exception cex) {
throw new RuntimeException("LockDListNode class not found");
}
}
Again, ugly error handling. But, at least we get to do this now!
protected LockDListNode newNode(Object item, DListNode prev, DListNode next) {
try {
return factory.getNewNode(item, prev, next);
} catch (Exception e) {
throw new RuntimeException("New node could not be created");
}
}
Except that is WAY worse than what you had before. It is much less legible and can throw new runtime errors. We also can't effectively (or at least I didn't come up with a way to) remove the casting from remove and lockNode (although because lockNode only applies to locked lists, you could probably make that take a LockDListNode).
The long and short of it is that you'll have to do a lot of generic hacking to remove 3 casts; casts that make sense and work. The generics don't make sense and might not work. And most importantly, because of type erasure, generics will use type casting under the hood anyway to ensure type safety.
More good reading on type erasure