1

As I understand the semantics of a custom constructor may be typically added to a class via a companion object. Is there then, any way to inherit a custom constructor while inheriting a class?

On the one hand I have found that companion objects are not synthetically inherited along a case class, and on the other, I am not aware of a way of creating custom constructors inside a class itself, so that they are inherited. And yet inheriting custom constructors seems to be a perfectly valid use case to me. So is it supported in some (straightforward) way in Scala?

A naive demonstration of intent:

class A {}
object A {
  def apply(n: Int) = {
    println(n)
    new A
  }
}

class B extends A {}

object Test {
  val a1 = A
  val a2 = A(3)
  val b1 = B    // compile error
  val b2 = B(3) // compile error

P.S. I have even found the arcane/deviant technique of defining this custom constructors result in a custom constructor that does not in actuality get inherited (it does work for just creating custom constructors, but quite oddly and unfortunately those do not get inherited). Demonstrating code:

class A {
  def this(n: Int) = {
    this
    println(n)
  }
}

class B extends A {}

object Test {
  val a1: A = new A
  val a2: A = new A(3)
  val b1 = new B    
  val b2 = new B(3) // compile error
}

Clarification of Intent Edit:

consider "constructor" and "companion factory methods" interchangeable for the sake of this question.

3
  • 1
    What you're describing as a constructor here isn't actually a constructor. It's just a function application on the companion object (which is internally calling the constructor). Commented Oct 2, 2015 at 15:33
  • Thanks, okay, call it a factory method. Isn't that the only technique for accomplishing the semantics of a custom constructor / multiple constructors, in scala? Commented Oct 2, 2015 at 15:33
  • By the way, for a type parameterized class, not an extended one, a this custom constructor of the style above works, while a companion object does not. Commented Oct 3, 2015 at 18:41

3 Answers 3

2

You can't inherit constructors directly, and because you can't you also can't inherit things that use them without a little bit of work. But you can abstract away anything beyond the constructor call.

Let's suppose we have

class Foo(text: String) {
  override def toString = "Foo: " + text
}
object Foo {
  def apply(text: String) = new Foo(text)  // Auto-generated for case class
  def apply(i: Int) = new Foo(
    if (i > 0) i.toString
    else if (i == 0) ""
    else s"negative ${0L - i}"
  )
}

and we then decide to

class Bar(text: String) extends Foo(text) {
  override def toString = "Bar: " + text
}

Now, what do we do about object Bar? Instead of writing all the logic over again, we create a trait to separate and abstract the object creation from the computation of the constructor parameter(s):

trait FooCompanionLike[A <: Foo] {
  def apply(text: String): A    // I am abstract!
  def apply(i: Int): A = apply(
    if (i > 0) i.toString
    else if (i == 0) ""
    else s"negative ${0L - i}"
  )
}

Now we can

object Foo extends FooCompanionLike[Foo] {
  def apply(text: String) = new Foo(text)
}
object Bar extends FooCompanionLike[Bar] {
  def apply(text: String) = new Bar(text)
}

So you can't completely escape boilerplate, but you can reduce it to extending from a trait and a single method call.

If you do it this way (where the abstract apply perfectly matches the constructor), you can even get case classes to work without manually defining the abstract apply method in the companion:

case class Baz(text: String) extends Foo(text) {
  override def toString = "Baz: " + text
}
object Baz extends FooCompanionLike[Baz] {
  // Nothing here!  Auto-generated apply works!
}
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks. Does Java exhibit the same inheritance limitation?
Maybe reverting to a type-parameterized factory class would be an equivalent workaround.
@matt - That's effectively what the companion objects are. It's just more awkward in Java because there's more to do manually.
Could we not further this and make create concrete in FooCompanionLike thus ridding ourselves of the redundant implementations in the Foo and Bar objects, with some ClassTag magic? am I right saying that we still need to both inherit the class and hard-code a factory object for it? If so I might in some cases workaround my original intent by using only a factory class, which will contain (and allow access to) the class it is creating, thereby just one inheritance for users in this scheme, but maybe this brings along some other limitations. Anyway, thanks again!
I see you got rid of the _text parameter, but I actually thought the issue of shadowing parameters and the lack of a way to "pass through" the params in an invisible, automatic way really bears on the question, as it relates to primary ctor. Mentioning the companion trait in this context was clever and apt!
1

Short answer: no straightforward way; try to workaround and resist the desire.

1 Comment

This answer is shorter and more sarcastic than I have managed.
0

Constructors in Scala are defined in the body of the class and take parameters after the class name e.g.

class A(i: Int) {
  println(i)
}

The println(i) in this case is the constructor logic. If you now extend A, like this:

class B(i: Int) extends A(i)

and instantiate B, val b1 = new B(2) you'll see that the constructor is indeed inherited.

As you've already found out, Scala allows you to define alternative constructors by defining functions called this. But these alternative constructors must call the primary constructor.

The way I understand it is that there is really only one constructor for any Scala class, the alternative constructors just filter into it. For example:

class A(x: Int, y: Int) {
  // do some constructing!

  def this(x: Int) = {
    this(x, 1) // provide a default value for y
  }
}

4 Comments

Thanks but this doesn't address the question's intent (maybe only @resueman's technical comment), which is accomplishing an inheritable factory method, and a plurality of such ones.
I'm not sure it makes sense to inherit a factory method. A factory method on class A is a function that takes 0 or more params and returns an A e.g. (...) => A. If B extends A and the factory function was inherited it would still return an A not a B. You would have to define a new function (...) => B
I'm afraid once again, your comment is about the technical terms not the semantics of the intent. Thanks anyway.
Constructors are indeed not inherited. The semantics of new are defined in "template evaluation" scala-lang.org/files/archive/spec/2.11/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.