If I have some class heirarchy defined like this
sealed trait A
case class B() extends A
case class C() extends A
// and so on ..
and in a different location I want to call some method dynamically passing above classes as type to that function
(eg: method[B](..){..} or method[C](..){..})
How can this be achieved at runtime?
Related
I wrote some code for Logic Expressions in Scala and I was wondering if I could find a way to mix in a trait into an abstract class, so I don't have to extend it in every subclass. Here is what I've got:
abstract class LogicExpression
case class Literal(lit:String, not:Boolean) extends LogicExpression with Ordered[Literal]
case class Conjunction(lits:Seq[Literal]) extends LogicExpression with Ordered[Conjunction]
...
I want every case class to be only Comparable to another instance of itself (Literal with Literal, Conjunction only with Conjunction, etc) but I would like to extend the trait in the abstract class so I don't have to repeat it on every subclass. Is this possible?
I tried
abstract class LogicExpression extends Ordered[LogicFormula]
but that would also allow comparing Literal with Conjunction for example.
You need something like this:
sealed trait LogicExpression[T <: LogicExpression[T]] extends Ordered[T]
case class SomeExpression() extends LogicExpression[SomeExpression] {
override def compare(that: SomeExpression) = ???
}
Let's say we have two traits:
trait Trait1
trait Trait2
If I try something like val single = new Trait1 I get an error error: trait Trait1 is abstract; cannot be instantiated. However, val twoTraits = new Trait1 with Trait2 compiles. Why is it so?
P.S. I also observed that val single = new Trait1 {} compiles just fine. Could you provide a proper explanation?
Technically, you can't instantiate a single trait or multiple mixed traits directly, but the compiler uses some syntactic sugar that allows you to create anonymous classes that extend them. Let's say we have:
trait A
trait B
When you call new A with B, what's really happening is that the compiler is creating an anonymous class that mixes in both A and B. You get:
final class $anon extends A with B
new $anon()
When you call new A {}, the same thing happens. You get an anonymous class that extends A:
final class $anon extends A
new $anon()
The only difference is syntactical. When creating an anonymous class from a single trait, you are required to at least use braces {} to distinguish it from a class. That is, it is easier to discern whether the template can be constructed, or must be wrapped in an anonymous class to be constructed. With multiple traits (or even a class with traits mixed in), the compiler understands it will always need to create an anonymous class first.
To summarize:
class C
new A {} // Anonymous class that extends A, then constructed
new A with B // Anonymous class that extends A with B, then constructed
new C // Constructed instance of class C
new C with A // Anonymous class that extends C with A, then constructed
While trying to learn Akka, I often find examples with a class hierarchy similar to this:
sealed trait Message
case class TextMessage(user: String, text: String) extends Message
case class StatusMessage(status: String) extends Message
However, in the Scala docs there's a following example:
abstract class Notification
case class Email(sourceEmail: String, title: String, body: String) extends Notification
case class SMS(sourceNumber: String, message: String) extends Notification
case class VoiceRecording(contactName: String, link: String) extends Notification
What's the difference in using a sealed trait vs. an abstract class (or sealed abstract class in this case) as a base class without constructor parameters for a class hierarchy? Are there some advantages in using one over the other?
Edit:
Specifically, if both, the trait and the abstract class are sealed, I can't extend them outside the file, right? In that case I couldn't inherit from them in Java either? If that's the case, being sealed would render most of the arguments found in the suggested duplicate useless since they refer to inheritance outside the file.
In this particular case there are no differences except that you can't extend multiple abstract classes but you can extend multiple traits.
You should check other answers (as mentioned in the comments) to see the actual differences between abstract classes and traits. If you are just going to use an abstract class or a trait to define the type hierarchy as in this case, then there are no differences.
E.g. you could to the following:
trait A
trait B
case class C(a: Int) extends A with B
but you can't do:
abstract class A
abstract class B
case class C(a: Int) extends A with B
I try
trait Foo[A] {
def copy(int: Int): A
}
case class Bar(int: Int) extends Foo[Bar]
but I get
error: class Bar needs to be abstract, since method copy in trait Foo of type (int: Int)this.Bar is not defined
Since Bar is a case class, it automatically defines a copy method with exactly this signature.
Why doesn't the Foo class satisfy the interface defined by the trait Bar?
I am quoting the Scala's specification:
A method named copy is implicitly added to every case class unless the class already has a member (directly defined or inherited) with that name, or the class has a repeated parameter.
I need to define a val in my companion object which is initialized with a method which takes the companion class as parameter.
I want to handle this with traits to not repeat myself. My Problem ist, that X.getClass ist not the same as classOf[X]. The first is the class of the companion object and the second is the class of the companion class, but I need to get the companion class without hardcoding it directly.
Basically I need something like this:
trait Foo {
}
object FooCompanionObject[f <: Foo] {
val fClazz = classOf[f]
}
// Bar's fClass should be classOf[Bar]
case class Bar extends Foo;
object Bar extends FooCompanionObject[Bar];
The problem is that I cannot get the class of an generic type due to type erasure
There are several problems in your code. First, as you already said, the type will be erased, second objects (object FooCompanionObject[f <: Foo]) don't take type parameters and third, objects can not be extended (object Bar extends FooCompanionObject). To do what you want, you have to create an abstract base class for your companion objects, that takes a type parameter, which may be constrained to a specific type if you like, and has to be context bound on ClassTag. From the ClassTag you can then get the runtime class by calling runtimeClass on it. The final solution could look like this:
import scala.reflect.ClassTag
import scala.reflect.classTag
trait Foo
abstract class Companion[A <: Foo : ClassTag] {
val fClazz = classTag[A].runtimeClass
}
class Bar extends Foo
object Bar extends Companion[Bar]
scala> Bar.fClazz
res2: Class[_] = class Bar