MBearr1221
MBearr1221

Reputation: 13

Passing and Accessing Values and Functions of a Subtype that was passed into a Class Constructor requiring it's Supertype

I'll keep this as simple as possible. Let's say I have a parent class with a function as below that takes a position argument as a Point data class

open class Obj(val pos:Point) {
    fun foo() : Double {
        return 5.0 + pos.x * pos.y * pos.z
    }
}

For sake of thoroughness, here is the Point data class

data class Point(val x:Double, val y:Double, val z:Double)

So I have multiple children that inherit from the Obj class but implement an additional function that is named the same in every child and calls a function in the parent,

class Cube(pos:Point) : Obj(pos) {
    fun number() : Double {
        return 10.0 * foo()
    }
}

class Sphere(pos:Point) : Obj(pos) {
    fun number() : Double {
        return 20.0 * foo()
    }
}

My question is, if I have a function somewhere that takes in objects that inherit from Obj but not Obj itself, how can I ensure they are of their own subtype rather than the Supertype?

For example, I currently have my function looking like this

fun foo2(obj:Obj) {
    println(obj.number()) // throws error because obj doesn't have method number()
}

Upvotes: 1

Views: 59

Answers (2)

MBearr1221
MBearr1221

Reputation: 13

Okay so using the suggestion from Animesh Sahu, I've implemented an Interface called ObjI in the base class, and required each implementation override the number() function. I combined that with the answer given by gidds, suggesting creating a function that calls another function. So the number() function in the base class just calls the foo() function

data class Point(val x:Double, val y:Double, val z:Double)


interface ObjI {
  fun number() : Double
}

open class Obj(val p:Point) : ObjI {
  override fun number() = foo()
  
  fun foo() : Double {
    return 5.0 + p.x * p.y * p.z
  }
}

class Sphere(p:Point) : Obj(p) {
  override fun number() : Double {
    return 10.0 * super.foo() 
  }
}

class Cube(p:Point) : Obj(p) {
  override fun number() : Double {
    return 20.0 * super.foo()
  }
}

fun main(args: Array<String>) {
  val s = Sphere(Point(13.0, 6.0, 1.0))
  val c = Cube(Point(13.0, 6.0, 1.0))
  printem(s)
  printem(c)
}

fun printem(o:Obj) {
  println(o.number())
}

Upvotes: 0

gidds
gidds

Reputation: 18627

The normal approach to this sort of case would be to add an abstract method to the base class, and have the subclasses implement it. That way, whenever you have a base class reference, the compiler knows that method is available.

That would require the base class itself to be abstract, so you can't instantiate it directly. (And if foo() is only used by number() implementations, it might make sense to hide it from other classes.)

abstract class Obj(val pos: Point) {
    abstract fun number(): Double

    protected fun foo() = 5.0 + pos.x * pos.y * pos.z
}

class Cube(pos: Point) : Obj(pos) {
    override fun number() = 10.0 * foo()
}

If, however, you need the base class to be instantiable, then that's more tricky: there's no easy way to specify 'only subclasses'. Depending on your exact requirements, you might allow the base class to be passed, and have it provide a default implementation of number():

    open fun number() = foo()

Upvotes: 0

Related Questions