Given simple class hierarchy
abstract sealed class Base(id: String)
case class Child1(id: String, value: Int) extends Base(id)
case class Child2(id: String, value: Long, file: File) extends Base(id)
can I use macros or something like that to avoid passing id to Base (and instruct compiler to generate this for me)? With single argument it's not that hard to pass it, but in case of several arguments it becomes uncomfortable.
Or if I could omit specification of id in child classes and make compiler generating ones for me from base class?
You can make Base as a trait:
sealed trait Base {
val id: String
}
case class Child1(id: String, value: Int) extends Base
case class Child2(id: String, value: Long, file: File) extends Base
Related
This question already has an answer here:
If case class inheritance is prohibited, how to represent this?
(1 answer)
Closed 5 years ago.
I am trying to inherit a class in Scala. My Parent class is
case class Person (name:String, age:Int, valid:Boolean)
My child class is
case class MarriedPerson (override val name: String,
override val age: Int,
override val valid: Boolean,
spouse: Person) extends Person(name, age, valid)
When I try this, I get an error saying
:13: error: case class MarriedPerson has case ancestor Person, but case-to-case inheritance is prohibited. To overcome this limitation, use extractors to pattern match on non-leaf nodes.
Why is this the case and how I get around this to get a case class to inherit another case class?
If I remove the "case" in the parent class, I get an error saying
that
:15: error: value name overrides nothing
override val name: String,
Why can't a case class not inherit from a normal class in this case?
You cannot inherit from one case class to another. In theory its possible to inherit from a case class to a non-case class, but that's not recommended (which is why it's a good practice to make your case classes final - more on the topic in this post).
Case classes have, among other peculiarities, that of exposing publicly all of the constructor arguments. This does not happen for normal classes, which is why to make your snippet of code work you should just prepend all of Person's arguments with val.
Thus, the following is valid syntax:
class Person(val name: String, val age: Int, val valid: Boolean)
case class MarriedPerson(
override val name: String,
override val age: Int,
override val valid: Boolean,
spouse: Person) extends Person(name, age, valid)
However, for your information, it is also quite common to use traits for this kind of situations, which allows for greater flexibility (traits support multiple inheritance) and a slightly less verbose syntax, as follows:
trait Person {
def name: String
def age: Int
def valid: Boolean
}
case class MarriedPerson(
name: String,
age: Int,
valid: Boolean,
spouse: Person) extends Person
If you are curious on why it's possible to define defs in traits that are overridden by vals in sub-classes, it's because Scala implements the Uniform Access Principle.
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'm getting my feet wet in generic programming with Shapeless and I'm running into usual beginner problems.
I actually wrote a piece of code that applies a polymorphic function to case classes that compiles and works correctly under Scala IDE but fails to compile under sbt. While trying to minimize the issue I came all the way down the basic example in https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/generic.scala and my problems with creating Generic instances still persisted.
I'd really appreciate an explanation why this compiles:
object Example {
sealed class Animal
case class Cat(name: String) extends Animal
case class Dog(name: String) extends Animal
val generic = shapeless.Generic[Animal]
}
while this:
sealed class Animal
case class Cat(name: String) extends Animal
case class Dog(name: String) extends Animal
object Example {
val generic = shapeless.Generic[Animal]
}
and this:
sealed class Animal
case class Cat(name: String) extends Animal
case class Dog(name: String) extends Animal
object Animal {
val generic = shapeless.Generic[Animal]
}
fails to compile with:
could not find implicit value for parameter gen: shapeless.Generic[example.Example.Animal]
not enough arguments for method apply: (implicit gen: shapeless.Generic[example.Example.Animal])shapeless.Generic.Aux[example.Example.Animal,gen.Repr] in object Generic. Unspecified value parameter gen.
I'm using shapeless 2.3.0 with Scala 2.11.8.
Thanks is advance!
I am probably doing something wrong.
When I extend a class, I specify the mapping between the extendee's constructor and the extended class constructor:
class Base(one: String, two: String)
case class Extended(one: String, two: String, three: String) extends Base(one, two)
How can I instead suffice with something like any of the following, thus implying a "default" mapping?
class Base(one: String, two: String)
case class Extended(one: String, two: String, three: String) extends Base
class Base(one: String, two: String)
case class Extended(three: String) extends Base
I am probably missing the cleaner way of just adding a parameter without all that ceremony.
Or should I be using a trait rather than subclassing, for such simple thing....
All the parameters of a case class generated apply method have to be specified in the case class declaration, so your second proposal cannot work. The first one can be accomplished if Base is abstract, using abstract vals:
abstract class Base {
// no initializers!
val one : String
val two : String
def print { println("Base(%s, %s)" format (one, two)) }
}
// Note: constructor arguments of case classes are vals!
case class Extended(one: String, two: String, three: String) extends Base
...
Extended("foo", "bar", "baz").print // prints 'Base(foo, bar)'
However, the names need to match exactly.
I'd like to "lock" a class, which is extended from a trait. Is it possible in Scala?
For example I have:
trait A {
val boris: String
val john: String
val number: Int
}
class B extends A {
// do something with these values
}
but can I ensure, that in class B no new values will be added if those aren't declared in trait A?
Thanks for your answers.
You cannot.
But if you simply mark the trait as sealed and provide a default implementation:
sealed trait A { val boris: String }
final class B(val boris: String) extends A {}
then people are free to create implicit value classes that make it look like new functionality has been added (except without actually creating the class):
implicit class MyB(val underlying: B) extends AnyVal {
def sirob = underlying.boris.reverse
}
(new B("fish")).sirob // "hsif"
You can also let the classes take a type parameter as a marker if you want to keep them straight at compile-time (though not runtime):
sealed trait A[T] { val boris: String }
final class B[T](val boris: String) extends A[T] {}
implicit class MyB(val underlying: B[Int]) extends AnyVal {
def sirob = underlying.boris.reverse
}
(new B[Int]("fish")).sirob // "hsif"
(new B[Char]("fish")).sirob // error: value sirob is not a member of B[Char]
So you could--especially with 2.10--simply lock everything and let users enrich the original interface this way.
I'm not sure if this covers your intended use case, though; it doesn't provide any inheritance.
Based on your example and my guess at what you are actually trying to do, you may want to consider just using case classes.
Extending a case class is generally avoided (I think it will spit out deprecation warnings if you try), so that will prevent people from wanting to extend your class in order to add functionality.
Translating your example into a case class:
case class A (boris: String, john: String, number: Int)
Then instead of extending A to change its values, you'd just make a new instance, e.g.
val a2 = someOtherA.copy(john="Doe")