16

I've defined multiple constructors, with some default argument values in all of them. Looks correct (I can't see any ambiguity), but Scala (2.8) compiler complains:

multiple overloaded alternatives of constructor define default arguments

Does it mean that I can't define default values for overloaded constructors at all?

Let me illustrate the situation (primitivized, of course, but illustrative):


class A(subject : Double, factor : Int = 1, doItRight : Boolean = true) {

  def this (subject : Int, factor : Int = 1, doItRight : Boolean = true) = {
    this(subject.toDouble , factor, doItRight)
  }

  def this (subject : String, factor : Int = 1, doItRight : Boolean = true) = {
    this(subject.toDouble , factor, doItRight)
  }

  def this () = {
    this(defaultSubject)
  }

}



Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
Ivan
  • 63,011
  • 101
  • 250
  • 382

3 Answers3

11

Taken straight from the compiler's source code:

// only one overloaded alternative is allowed to define default arguments

In general, I wouldn't advise that you mix overloading and defaults. Even if there's no conflict it can make your code harder to read.

UPDATE

Since you added code, it's clear now that you don't want/need to override the default values for each secondary constructor. In your particular case, I might even question the need for those extra constructors at all; Int=>Double is already available for you as an implicit conversion and String=>Double looks like you might be perverting the type system :)

Also... As an alternative to overloaded constructors, you can define just the primary constructor with defaults, then overload the apply method of the companion object and use that as a factory. This is of course completely optional, but it's rapidly becoming established as a pattern through the use of case classes.

Kevin Wright
  • 49,540
  • 9
  • 105
  • 155
  • 1
    Using factories seems pretty unnecessary in the case, and violating Occam's razor. It'd be even prettier to implement constructors for all the cases (manually applying default values) IMHO, and so I've done (looks too oldie and self-repeating though). – Ivan Oct 10 '10 at 10:10
  • I would take a hard look at my code, if I would need these amounts of constructors. I hardly need a single one these days... – soc Oct 10 '10 at 14:06
  • Please don't take my upvote of this answer to mean I approve of this language limitation. – FLGMwt May 24 '17 at 18:07
4

The overloading fails because you (unnessesarily) define multiple constructors with default values. Do this instead:

class A(subject : Double, factor : Int = 1, doItRight : Boolean = true) {

  def this (subject : Int) = {
    this(subject.toDouble)
  }

  def this (subject : String) = {
    this(subject.toDouble)
  }

  def this () = {
    this(defaultSubject)
  }
}
Mia Clarke
  • 8,134
  • 3
  • 49
  • 62
  • But don't factor and doItRight arguments need to be explicitly specified if subject Int or String in this case? – Ivan Oct 10 '10 at 12:09
  • @Ivan, No, you don't need to specify them, as you've already set default values for them in the main constructor. – Mia Clarke Oct 10 '10 at 14:54
  • 1
    I guess it's because your code doesn't do what was expected at the first place. With your code, you can't do something like " new A("Subject", 98, false) " – frank Jun 16 '14 at 15:12
  • @frank, Yes you can. The primary constructor allows for that. – Mia Clarke Jun 17 '14 at 08:26
  • @MiaClarke Well, maybe I'm doing it wrong, but after testing a few time on scala 2.10, I get this : scala> val pa = new A("dsa", 89, false) :8: error: type mismatch; found : String("dsa") required: Double val pa = new A("dsa", 89, false) ^ – frank Jun 17 '14 at 14:23
  • "unnessesarily" is not something I'd say about some piece of code that may only serves to illustrate an issue. Let's say for instance that there's a last "verbose" argument, which may indeed be convenient to have as a default one to false. – Profiterole Apr 24 '22 at 18:10
0

Yes, that's not convenient, you'll need to do without default arguments in your auxiliary constructors like:

class X(n: Int, verbose: Boolean = false) {

  def this(n: String, verbose: Boolean) = this(
      Integer.parseInt(n),
      verbose
  )

  def this(n: String) = this(n, false)
}

Mind though that inline (within a function) with default arguments different from each other you can do that:

def main(args: Array[String]): Unit = {

    class Y(n: Int, dummyImplicit: DummyImplicit = null, verbose: Boolean = false) {

        def this(n: String, verbose: Boolean = false) = this(
            Integer.parseInt(n), 
            verbose = verbose
        )
    }
}
Profiterole
  • 167
  • 1
  • 4