abstract constructors or equivalent in kotlin

47 Views Asked by At

I have some code for a abstract Binary Tree class with nodes. I have an add(value : N) method and and add(value : E)that just takes an element and automatically creates the Node.

As you can see below the Node is generically typed when you create the tree, I want to change add(value : E) to a non-abstract method that will call the Nodes constructor with value : E as a parameter.

abstract class BinaryTree<E, N : BinaryTree<E,N>.Node>(var rootNode: N? = null) {

    abstract fun add(node : N) : N
    abstract fun add(value : E) : N


    abstract fun remove(value : E) : N?
    abstract fun remove(node : N) : N
    open fun removeAll(value: E): List<N> {
        val removedNodes: MutableList<N> = mutableListOf()
        var removedNode: N?
        while (remove(value).also { removedNode = it } != null) {
            removedNodes.add(removedNode!!)
        }
        return removedNodes
    }

    abstract inner class Node(var data: E, var left: N?, var right: N?){

        constructor(data: E) : this(data, null, null) {}

        open fun isLeaf(): Boolean{
            return (left == null && right == null)
        }
    }
}

I have looked into if there's anything like an abstract constructor that would force the implementation of a constructor(value : E) for all subclasses, however I don't think that's a thing.

I'm not sure if there's a way to create that in kotlin. I'm not sure if there's a different solution that would accomplish the same thing.

My problem with leaving it abstract is that it removes the possibility for implementation while leaving subclasses the opportunity to be subclasses further since I would have to remove generic Node typing out in order to call a constructers of that subclass. For example in this implementation BinarySearchTree still deals with a generic Node which is a BSTNode but is not abstract so you can't just pass it on to the next subclass:

class BinarySearchTree<E : Comparable<E>, N : BinarySearchTree<E,N>.BSTNode>(rootNode: N? = null) : BinaryTree<E, N>(rootNode) {

    override fun add(value : E): N {
        //This can't happen without a generic Node constructor or removing the generic Node type from my default constructor which limits subclasses.
        //I want to be able to call add(N(E)) where N(E) is whatever N's constructor is
    }

    override fun add(node : N) {}

inner class BSTNode(data: E, val parent: N?, left: N? = null, right: N? = null)
        : BinaryTree<E, N>.Node(data, left, right), Comparable<N> {

    //I want to force this in here
    constructor(data: E) : this(data, null, null, null) {}
1

There are 1 best solutions below

3
Joffrey On

The node constructor could be a function that subclasses of the tree pass to the parent tree class.

abstract class BinaryTree<E, N : BinaryTree<E,N>.Node>(
    var rootNode: N? = null,
    val createNode: (value: E) -> N,
) {

    abstract fun add(node : N) : N
    
    fun add(value : E) : N = add(createNode(value))

    // ...
}

class SomeTreeClass(
    rootNode: N? = null,
) : BinaryTree<MyValue, MyNode>(rootNode, createNode = { MyNode(it) }) {

    // ...

}