Reputation: 413
I'm trying to use pattern matching to match classes that extend from a parent class, but also restrict the classes you can pass as a parameter using classType: Class[_ <: Animal]
. Here's an example:
class Animal
class Dog extends Animal
class Cat extends Animal
def returnAnimalSubtypeObject(classType: Class[_ <: Animal]): Animal = {
classType match {
case _: Dog => new Dog()
case _: Cat => new Cat()
}
}
returnNewClass(Class[Dog])
But this returns the following error message:
<console>:16: error: pattern type is incompatible with expected type;
found : Dog
required: Class[_$1] where type _$1 <: Animal
case _: Dog => new Dog()
I think I'm not understanding something fundamental here since I'm still a beginner. Could someone explain what's the best approach to solving this?
Upvotes: 0
Views: 1103
Reputation: 40500
At the high level, doing what you are trying to do does not seem very useful even if it did work: returnNewClass(Class[Dog])
does not seem to have any advantages over just new Dog
.
With that caveat, a proper way to define that function would be something like this:
import scala.reflect.ClassTag
def animal[T <: Animal : ClassTag]() = implictly[ClassTag[T]].runtimeClass.newInstance
Two interesting things here:
[T <: Animal : ClassTag]
defines a type parameter T
that must be a subclass of Animal
and have a ClassTag
avaialable for it at call site.
This definition is equivalent to def animal[T <: Animal](implicit ct: ClassTag[T])
.
ClassTag
is a scala object that provides a link between a scala type and the corresponding java class that implements it. So, once the ClassTag
is avaialable, you can get a handle to .runtimeClass
from it, and then instantiate it using java reflection api.
The way to call that function would be val d = animal[Dog]
, though, I'll say again that I just don't see how this adds any value over just new Dog()
...
Upvotes: 1
Reputation: 1052
It seems like you are trying to implement something like ADT(Abstract Data Type). I would prefer to use sealed trait
or sealed abstract class
since it's better in pattern matching.
see also: https://nrinaudo.github.io/scala-best-practices/definitions/adt.html
For purpose of understanding, I change some code here.
sealed trait Animal
case class Dog(name: String) extends Animal
case class Cat(name: String) extends Animal
object Animal {
def description(animal: Animal): String = animal match {
case Dog(name) => s"this is a dog named $name, it can bark"
case Cat(name) => s"this is a cat named $name, it can not bark like a dog"
}
}
scala> Animal.description(Dog("dog"))
val res0: String = this is a dog named dog, it can bark
scala> Animal.description(Cat("cat"))
val res1: String = this is a cat named cat, it can not bark like a dog
When we use sealed
keywork, the Scala compiler will check the pattern match is exhaustive or not.
// if we miss a type to match
def returnAnimalSubtypeObject(x: Animal): Animal = x match {
case d: Dog => d
}
// it will show warning.
match may not be exhaustive.
[warn] It would fail on the following input: Cat(_, _)
[warn] def returnAnimalSubtypeObject(x: Animal): Animal = x match {
[warn] ^
[warn] one warning found
Upvotes: 0
Reputation: 20551
The Class
object for Dog
is not a Dog
: it's a Class
.
With the proviso that there's almost certainly a better way to accomplish what you actually want to do, you could:
def returnAnimalSubtypeObject(clazz: Class[_ <: Animal]): Animal =
if (clazz == classOf[Dog]) new Dog()
else if (clazz == classOf[Cat]) new Cat()
else new Animal()
Upvotes: 1