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.
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.
I have a function that gets a string with name of model and returning an instance of that case class, but im wondering what is the correct way to use dynamic return type here in this function?
case class Person(fname: String, lname: String)
case class Animal(type: String, weight: Double)
def getInstanceOf(model: String): ??? = model match {
case "person" => Person("jack", "robinson")
case "animal" => Animal("lion", 190.0)
}
thanks!
It may help to have a bit more context of what the end goal is here to suggest a solution.
If your models all conform to a particular classification and you don't need to know exactly what instance they are then the abstract class (or trait) suggestion above should work.
EDIT: my original answer included this snippet:
You could also make the return type generic and put a type boundary on it in this scenario, such as:
def getInstanceOf[T <: Living](model: String): T = ...
But as pointed out below this allows the caller to specify what type they expect which may counteract what is contained in the model argument
If you only returned one of two possible types but needed the exact type then maybe an Either[T1, T2] would be appropriate here (http://www.scala-lang.org/api/current/scala/util/Either.html) such as:
def getInstanceOf(model: String): Either[Person, Animal] = ...
But again I think it would be easier to answer if the end goal was explained a bit
Maybe you can define an abstract class and extends both classes from it, and then you can specify the abstract class as the return type:
abstract class Living
case class Person(fname: String, lname: String) extends Living
case class Animal(Type: String, weight: Double) extends Living
def getInstanceOf(model: String): Living = model match {
case "person" => Person("jack", "robinson")
case "animal" => Animal("lion", 190.0)
}
// getInstanceOf: (model: String)Living
I have an abstract base class, Foo, whose constructor I'd like to have an optional parameter. If none is provided, I'll just give it a None value.
A source Foo will not have parents, so I'd just like to construct them without a list of parents (leave default value for parent list)
A derived Foo might have provided parents, so I'd like to mimic the signature of the Foo base class.
Below is my attempt:
abstract class Foo(val id: String, var parentIds: Option[List[String]]=None) { }
case class SourceFoo(override val id: String)
extends Foo(id, parentIds=None) { }
case class DerivedFoo(override val id: String,
override var parentIds: Option[List[String]])
extends Foo(id, parentIds) { }
I'm getting a compiler error that a mutable variable cannot be overridden (referencing the parentIds in the DerivedFoo constructor.
This list is subject to change, so I don't want to make it a val (which removes my compiler issues).
This is a very basic OO issue, so it must be simpler than I seem to be making it. How can I achieve my desired behavior idiomatically?
I managed to fix this after reading the documentation:
The constructor parameters of case classes are treated as public values and can be accessed directly.
Since my base class is abstract, I can simply extend it with default, val construction.
I simply need to specify that parentIds is a var in the DerivedFoo constructor.
abstract class Foo(id: String, parentIds: Option[List[String]]=None) { }
case class SourceFoo(id: String) extends Foo(id) { }
case class DerivedFoo(id: String, var parentIds: Option[List[String]]=None)
extends Foo(id, parentIds) { }
Here is another probably better way to go about it. Explictly acknowledge the difference between class parameters and class members. You also can make them private members if you like following this block of code.
abstract class Foo(identifier: String, parentIdentifiers: Option[List[String]]) {
val id = identifier
var parentIds = parentIdentifiers
}
case class SourceFoo(override val id: String) extends Foo(id, parentIdentifiers = None) { }
case class DerivedFoo(identifier: String, parentIdentifiers: Option[List[String]]) extends Foo(identifier, parentIdentifiers) { }
After that, you can create DerivedFoo and refer to the members as you are probably expecting, and you won't have two members with different names.
REPL output:
scala> DerivedFoo("1", Some(List("200","201","202")))
res0: DerivedFoo = DerivedFoo(1,Some(List(200, 201, 202)))
scala> res0.parentIds
res1: Option[List[String]] = Some(List(200, 201, 202))
scala> res0.parentIds = Some(List("800", "801", "802"))
res0.parentIds: Option[List[String]] = Some(List(800, 801, 802))
I think you can achieve your goal by changing the name of the parameter in the abstract class as follows.
abstract class Foo(val id: String, var parentIdentifiers: Option[List[String]]) {
parentIdentifiers = None
}
case class SourceFoo(override val id: String)
extends Foo(id, parentIdentifiers = None) { }
case class DerivedFoo(override val id: String,
var parentIds: Option[List[String]])
extends Foo(id, parentIds) { }
For the mutation, you can import scala.collection.mutable and use mutable.ListBuffer instead of List.
I assume of course, that you won't change the parentIds of a DerivedFoo instance from Some to None.
This will allow you use vals but still have mutable state.
But I wouldn't say mutable state is idiomatic Scala.
You usually use immutable val and List, and just copy the object whenever you want to change the list.
val fooA = SourceFoo("a")
val fooB = DerivedFoo("b", "a" :: Nil)
val fooB2 = fooB.copy(parentIds = fooB.parentIds :+ "x")
So to be more idiomatic, the simplest you can do is
sealed abstract class Foo(val id: String, val parentIdsOpt: Option[List[String]])
case class SourceFoo(override val id: String)
extends Foo(id, None)
case class DerivedFoo(override val id: String, val parentIds: List[String])
extends Foo(id, Some(parentIds))
Which is pretty close to what you had.
Note that DerivedFoo.parentIds isn't Option anymore, because DerivedFoo always has parents, so you don't have to deal with Option. (You still have to deal with empty list, though)
Also note the sealed keyword on the trait, which is not required, but recommended if you want to match on an instance of the abstract class or trait. (You can use sealed only if you own all the sub-classes, which seems to be the case in your example)
Is it possible to have a duck-typing style conversion where a converter will accept an arbitrary object and will try to convert it to the destination type?
case class RequiredFields(name: String, surname: String)
case class Person(name: String, surname: String, age: Option[Int])
implicit def arbitraryToPerson(object: ????): Person = Person(object.name, object.surname, None)
Arbitrary object doesn't have to be related to RequriedField class in any way, just the fields must exist.
I think you can combine structural typing and implicit conversions. Like so:
type RequiredFields = {
def name: String
def surname: String
}
case class NotAPerson(name: String, surname: String)
case class Person(name: String, surname: String, age: Option[Int])
implicit def arbitraryToPerson(o: RequiredFields): Person = Person(o.name, o.surname, None)
val p:Person = NotAPerson("oh", "my")
Are you sure that's a good idea, though? I think the much better pattern here would be to define a type class with the required fields. Have you considered that?
As hasumedic answered you probably want to use a trait.
If for some reason you don't want to use a trait (you don't have control over the package objects that are being sent to you etc) you can also use reflection to get the fields. (I don't actually know if this is a good idea, but my best guess is that it isn't)
case class RequiredFields(name: String, surname: String)
val rF = new RequriedFields("Hey", "yo")
val method = rF.getClass.getDeclaredMethod("name")
Then you get your value by invoking the getter for that field that scala makes for you
scala> val x = method.invoke(rF).asInstanceOf[String]
x: String = Hey
I think that you're looking for a trait:
trait Nameable {
def name: String
def surname: String
}
case class Person(name: String, surname: String, age: Option[Int]) extends Nameable
implicit def arbitraryToPerson(variable: Nameable): Person = Person(variable.name, variable.surname, None)
You can think of a trait like an interface. A characteristic of Scala traits is that they also accept implementations of its methods.
Classes extending this trait will need to implement the methods name and surname. In this case, the only valid Nameable parameter would be a Person. Having more classes that extend the trait would allow for some sort of polymorphism, making this implicit actually useful.
EDIT
From your comment and negative, just to point out that you can call your traits however you like. In your case, something like Nameable could work. It doesn't mean that there's a hierarchy, but that your object fulfils certain conditions, such as implementing those methods. Classes can extend more than one trait in Scala, using the with keyword.
case class Dog(name: String, surname: String) extends Animal with Nameable
In this case, your dog would be an animal, but you can enforce the implementation of the Nameable methods by extending the trait.
It seems you can do ducktyping driven conversion in the following way:
implicit def convertToPerson(obj: {def name(value: String): String; def surname(): String}): Person = {
Person(obj.name, obj.surname, None)
}
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