0

Code I have:

abstract class Animal {
   def init(animalType: String, jsonBlob: String)
}

class Dog (name: String, colour: String) {
   def init(animalType: String, jsonBlob: String) : Unit = {
        name = jsonBlob.name
        colour = jsonBlob.colour
    }
}

class Cat (address: String) {
   def init(animalType: String, jsonBlob: String) : Unit = {
        address = jsonBlob.address
    }
}

What I want: to instantiate a Cat or Dog dynamically.

I have made an attempt with code that looks like this:

case class AnimalDefinition(animalType: String, jsonBlob: String)
val animalDefinitions : Array[AnimalDefinition] = // 
val animals : Array[Animal] = animalDefinitions.map(new Animal(_.animalType, _.jsonBlob)) 

It should dynamically instantiate the correct class using the animalType parameter. I think traditionally I would do this using a case statement (if animalType == "Cat", return new Cat(..)). But I believe there's an automatic way to do this with reflections.

The code doesn't compile. I've tried reading the Scala reflection docs but they don't have examples that show dynamic instantiation of subclasses with additional parameters

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
user
  • 352
  • 3
  • 13
  • 2
    Why do you want to include reflection at all? What is wrong with just a simple factory function that instantes the correct type based on input? – Luis Miguel Mejía Suárez Oct 23 '19 at 01:03
  • @LuisMiguelMejíaSuárez so you are talking about something like this: https://alvinalexander.com/scala/how-to-create-factory-method-in-scala-apply-object-trait ? – user Oct 23 '19 at 22:29
  • Yeah, basically that. I would make the method return an **Option[Animal]** or an **Either[String, Animal]** to handle invalid entries. – Luis Miguel Mejía Suárez Oct 23 '19 at 23:14

1 Answers1

1

You can replace

if animalType == "Cat", return new Cat("cat name") ...

with

import scala.reflect.runtime.universe._
val mirror = runtimeMirror(getClass.getClassLoader)
val classSymbol = mirror.staticClass(animalType)
val typ = classSymbol.info
val constructorSymbol = typ.decl(termNames.CONSTRUCTOR).asMethod
val classMirror = mirror.reflectClass(classSymbol)
val constructorMirror = classMirror.reflectConstructor(constructorSymbol)
constructorMirror("cat name").asInstanceOf[Animal]
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • Thank you. At this point, I'm not sure if my desire to use reflection was appropriate. Why would one choose to use the second code block instead of the first line? – user Oct 25 '19 at 19:25
  • @user Well, if you have let's say 100 cases like `if animalType == "Cat", return new Cat("cat name")` then reflection can be better than boilerplate code. Surely for 2 cases there's not much sense. – Dmytro Mitin Oct 25 '19 at 23:02
  • Gotcha. I have about 10 cases at this point, but I might have to come back to this in the future. Thanks – user Oct 28 '19 at 16:26