Scala inherit parameterized constructor

19,157

Solution 1

You can just use a dummy name in the inherited class:

case class SomeHypothesis(anotherArg: SomeType, rq: Boolean = false, odays: Seq[Int] = Nil)
extends Hypothesis(rq, odays)

but you do have to repeat the default values. There is no need to override a val.

EDIT:

Note that your abstract class should not be a case class. Extending case classes is now deprecated. You should use an extractor for you abstract class instead:

abstract class SomeHypothesis(val request: Boolean)

object SomeHypothesis {
  def unapply(o: Any): Option[Boolean] = o match {
    case sh: SomeHypothesis => Some(sh.request)
    case _ => None
  }
}

Solution 2

In my mind the policy of default values doesn't belong in the base class but should go on the concrete classes. I'd instead do the following:

trait Hypothesis {
  def requirement: Boolean
  def onlyDays: Seq[Int]
  /* other common attributes as necessary */
}

case class SomeHypothesis(anotherArg: SomeType,
                          requirement: Boolean = false,
                          onlyDays: Seq[Int] = Nil)
  extends Hypothesis

The case class fields of SomeHypothesis will fulfill the requirements of the Hypothesis trait.

As others have said, you can still use an extractor for pattern matching on the common parts:

object Hypothesis {
  def unapply(h: Hypothesis): (Boolean, Seq[Int]) = (h.requirement, h.onlyDays)
}

Solution 3

I've spend DAYS bashing my head on the desk trying to understand why named params were not going into an extended class.

I tried traits, copy() you name it - me and the compiler were always at odds and when things did compile the values never got passed.

So to be clear if you have class

class A(someInt:Int = 0, someString: String ="xyz", someOtherString: String = "zyx")  

And you want to extend it:

class B extends A // does NOT compile 

class B(someInt: Int, someString: String, someOtherString: String) extends A // compiles but does not work as expected 

You would think that a call to B like so:

case object X = B(someInt=4, someString="Boo", someOtherString="Who") 

Would in fact either NOT compile (sadly it does) or actually work (it does NOT)

Instead you need to create B as follows (yes this is a repeat of an above answer but it wasn't obvious at first when google led me here...)

class B(someInt: Int, someString: String, someOtherString: String) extends A(someInt, someString, someOtherString) 

and now

case object X = B(someInt=4, someString="Boo", someOtherString="Who") 

Both COMPILES and WORKS

I have not yet worked out all the combinations and permutations of what/when and where you can put default values in the class B constructor but I'm pretty sure that defaults can be specified in the definition of class B with "expected" results.

Share:
19,157

Related videos on Youtube

flying sheep
Author by

flying sheep

Updated on February 12, 2020

Comments

  • flying sheep
    flying sheep over 4 years

    I have an abstract base class with several optional parameters:

    abstract case class Hypothesis(
        requirement: Boolean = false,
        onlyDays:   Seq[Int] = Nil,
        …
    ) extends Something {…}
    

    Do i really need to explicitly repeat all parameters with the additional keywords override val on top

    case class SomeHypothesis(
        anotherArg: SomeType,
        override val requirement: Boolean = false,
        override val onlyDays:   Seq[Int] = Nil,
        …
    ) extends Hypothesis(
        requirement,
        onlyDays,
        …
    ) {…}
    

    Or is there a syntax like

    case class SomeHypothesis(anotherArg: SomeType, **) extends Hypothesis(**) {…}
    

    I don’t even need anotherArg, just a way to pass all keyword args to the super constructor.


    I really like Scala’s idea about constructors, but if there isn’t a syntax for that one, I’ll be disappoint :(

    • Aaron Novstrup
      Aaron Novstrup about 13 years
    • flying sheep
      flying sheep about 13 years
      I’d downvote this comment if I could, since it is wrong. At least for me, this is not the case. See the first comment on this answer. I guess you should try it with default parameters. (Or my compiler does different things than yours, but I doubt that.)
    • Aaron Novstrup
      Aaron Novstrup about 13 years
      It has nothing to do with default parameters, but you're right: you do need override in this situation. Since you're using a case class, all of the constructor parameters implicitly have the val modifier. Because of the name clash, you have to add an explicit override modifier (and also an explicit val modifier, for the parser's happiness). My apologies for the hasty comment.
  • flying sheep
    flying sheep about 13 years
    But Mr compiler disagrees: overriding value requirement in class Hypothesis of type Boolean; value requirement needs `override' modifier (And when i add it, Mr compiler tells me that 'val' expected but identifier found.)
  • flying sheep
    flying sheep about 13 years
    I need it anyway, see my comment to the other answer. Also, they are always the same and have always the same default values, so I wouldn’t save more than the two parentheses from the abstract class’s constructor. And I want to save all the duplication.
  • flying sheep
    flying sheep about 13 years
    ah, now I see what you mean: Using other names saves me from having to override stuff. But that’s only a minor annoyance to the fact that I still have to write everything again for all subclasses. Not DRY at all >:(
  • axel22
    axel22 about 13 years
    Sorry, I missed the point that you were using case classes. But - you're not supposed to have an abstract case class anyway - extending case classes is a deprecated feature.
  • flying sheep
    flying sheep about 13 years
    What? Why? And what should I do instead?
  • axel22
    axel22 about 13 years
    A discussion on case classes inheritance deprecation: scala-programming-language.1934581.n4.nabble.com/…. You should use extractors: scala-lang.org/node/112
  • flying sheep
    flying sheep about 13 years
    My problem is still that I shouldn’t have to repeat myself. And I truly want to have a shared constructor interface that behaves the same in all subclasses. So every time I write name: Type = default, I’m repeating myself thrice. (should be 0)
  • Geoff Reedy
    Geoff Reedy about 13 years
    Then maybe they shouldn't be different subclasses. Instead there is one hypothesis class parameterized by extra data. Perhaps something like case class Hypothesis[T](info: T, requirement: Boolean = false, onlyDays: Seq[Int] = Nil) but perhaps with a type bound on T to ensure some common interface.
  • flying sheep
    flying sheep about 13 years
    sounds good. in fact, i need a requirement, an expectation and some filters (onlyDays, …). then i need to apply a function on the filtered data, which has to get grouped for some of these functions (the grouping criterion can be provided). then i need to evaluate the function result by an expectation (“in range 0 to 0.05”, “true”, “low”). the outcome of this evaluation can be used in requirements. (all parameters emphasized)