1

I am trying to add some processing logic in an overloaded constructor, but cannot seem to get it working.

Here is a simplified example of what I want to do:

class FooBar(val x: String, val y: String) {

    this(z: String) = {
        // DO SOME ARBITRARY CODE
        val x = // SOME ARBITRARY PROCESSING USING z
        val y = // SOME ARBITRARY PROCESSING USING z
        this(x, y)
    }
 }

However, I am getting a compilation error:

: 'this' expected but 'val' found

This is a very simple task in Java, I would simply set the x, y instance variables from 2 separate constructors.

Here is my Java equivalent of what I would like to accomplish:

class FooBar {

    public String x;
    public String y;

    public FooBar(String x, String y) {
        this.x = x;
        this.y = y;
    }

    public FooBar(String z) {
        // DO SOME ARBITRARY CODE
        this.x = // SOME ARBITRARY PROCESSING USING z
        this.y = // SOME ARBITRARY PROCESSING USING z
    }
}

=================== EDIT ==================

I decided to go with @om-nom-nom's approach using a companion object. However as @om-nom-nom pointed out there is no way to get around the missing new invocation. Therefore in order to make my constructors consistent, I overloaded the apply method in the companion object:

class FooBar(val x: String, val y: String)
object FooBar {
    def apply(x: String, y: String) = new FooBar(x, y)

    def apply(z: String) = {
        // DO SOME ARBITRARY CODE
        val x = // SOME ARBITRARY PROCESSING USING z
        val y = // SOME ARBITRARY PROCESSING USING z
        new FooBar(x, y)
    }
}

FooBar(someX, someY)
FooBar(someZ)
Sanketh Katta
  • 5,961
  • 2
  • 29
  • 30
  • 2
    `this(..)` can only be the first call in the constructor. – FThompson Oct 01 '13 at 22:09
  • @Vulcan I understand that. My question is how can I accomplish my task in Scala, given that restriction? How would you translate the Java code above into Scala? – Sanketh Katta Oct 01 '13 at 22:12
  • As for your edit, you could hide class constructor, so it will be only instantionable from within object: `class FooBar private[FooBar](val x: String, val y: String)`. This way `new FooBar(x,y)` is illegal everywhere else. – om-nom-nom Oct 01 '13 at 23:09

2 Answers2

5

It is usually done through the companion object (although it is possible to embed some expression as argument to the first-line-call, as @TheTerribleSwiftTomato shown):

class FooBar(val x: String, val y: String)
object FooBar {
    def apply(z: String) = {
        // DO SOME ARBITRARY CODE
        val x = // SOME ARBITRARY PROCESSING USING z
        val y = // SOME ARBITRARY PROCESSING USING z
        new FooBar(x, y)
    }
}

FooBar(someZ)

note that there is no new keyword on invocation and there will be no way to overcome this.

Community
  • 1
  • 1
om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
2

Fun fact: your Java "equivalent" is not equivalent per se. Here's something more like it:

class FooBar {

    public String x;
    public String y;

    public FooBar(String x, String y) {
        this.x = x;
        this.y = y;
    }

    public FooBar(String z) {
        // DO SOME ARBITRARY CODE
        this(x,y);
    }
}

This also won't compile, because in Java and in Scala any constructor call must be the first statement in an overloading constructor.

However, nothing stops you from using expressions as the argument values of the constructor call, like this:

class FooBar(val x: String, val y: String) {

    def this(z: String) = {
        this({"a"+"b"}, {null})
        // DO SOME MORE ARBITRARY CODE
    }
 }

Note that this is not exactly clean code, and a companion object approach, as per om-nom-nom's answer, is more maintainable in general.

Otherwise, a direct conversion from your Java template is only possible if you actually follow that template and use var members instead, e.g.:

class FooBar() {

    var x: String;
    var y: String;

    def this(x: String, y: String) = {
      this()
      this.x = x
      this.y = y
    }


    def this(z: String) = {
      this()
      // DO SOME ARBITRARY CODE
      this.x = // SOME ARBITRARY PROCESSING USING z
      this.y = // SOME ARBITRARY PROCESSING USING z
    }
 }
Community
  • 1
  • 1
mikołak
  • 9,605
  • 1
  • 48
  • 70
  • Thanks, the "Java Equivalent" was not meant to be the exact same as scala, but equivalent in what I am actually trying to accomplish. Correct me if i'm wrong, but from what I know there is actually no way to reproduce my Java code equivalently in Scala. – Sanketh Katta Oct 01 '13 at 22:22
  • 2
    @SankethKatta And if you don't believe linked answer, there is a [rule in Scala Language Specification](http://iainmcgin.github.io/scala-ref-markdown/ScalaReference-mathml.html#constructor-definitions): *A constructor expression is either a self constructor invocation [...] or a block which begins with a self constructor invocation.* – om-nom-nom Oct 01 '13 at 22:30
  • @SankethKatta : Assuming your Java code as the template, and that you're asking about the semantic equivalence of the API of the resulting class, this answer **is** equivalent. You end up with two constructors. And if you really want a direct translation, see my edit. Otherwise, it is as om-nom-nom says: no. – mikołak Oct 01 '13 at 22:33
  • Thanks guys! I believe it!, I'm just trying to wrap my head around the scala conventions for this scenario. I ended up using the companion object approach, its definitely the most understandable and maintainable. – Sanketh Katta Oct 01 '13 at 22:35