Is there any way to extend an object?
Asked Answered
E

8

64

In scala, we cannot extend object:

object X 
object Y extends X

gives an error error: not found: type X

In my case someone has defined some functionality in an object and I need to extend it (basically add another method). What would be the easiest way to extend this object?

Embosser answered 2/10, 2011 at 7:2 Comment(0)
S
95

As so often the correct answer depends on the actual business requirement. Extending from an object would in some sense defy the purpose of that object since it wouldn't be a singleton any longer.

What might be a solution is to extract the behavior into an abstract trait. And create objects extending that trait like so:

trait T{
    // some behavior goes here
}

object X extends T

object Y extends T {
    // additional stuff here
}
Savonarola answered 2/10, 2011 at 7:42 Comment(2)
That is what I ended up doing. It actually makes sense to not be able to extend an object due to the singleton requirement.Embosser
The problem with this is that X and Y have no subclassing relationship, which was the reason to subclass in the first place.Advocate
C
43

If you want use methods and values from another object you can use import.

object X{
  def x = 5
}

object Y{
  import X._
  val y = x
}
Crosswind answered 2/12, 2013 at 14:10 Comment(1)
My preferable way to. Also, I like how it composites in the code. Very readable.Obliging
M
14

You can't actually extend an object, because that would create two of it, and an object by definition exists only once (edit: well, that's not quite true, because the object definition can be in a class or method).

For your purposes, try this:

object X {
}

object Y {
    def a = 5
}

implicit def xToY(x: X.type) = Y

println(X.a)

It doesn't actually extend, but it does allow you to call new methods on it than were originally defined.

Monagan answered 2/10, 2011 at 7:18 Comment(9)
IMO, this is evil, never use implicit conversions like this or you are asking for troubles.Keister
First sentence is meaningless.Haim
@Mirco: Can you give an explanation for this?Strode
I would also like to know why this is evil.Embosser
@Strode @Embosser I think the problem is that when reading someone's code and seeing X.a it makes it harder to find out what X.a is referring to, because you need to know what implicits are in scope. At least that's what frustrates me about this. But I thought I'd suggest it anyway.Monagan
In general, implicit conversions should be used with care. As @Monagan pointed out, they do make your code harder to read. Often it's hard to know what implicit is being applied at a given location (remember that rules for implicit resolution are quite involved, and it might be far from easy to guess which one is being applied by only looking at the source. Though, things will definitely get better once IDEs support implicit highlighting). But in this case, the main reason is simply that X should really not be seen as Y, you really want to know which one you are manipulating.Keister
For the question that the OP is asking, the implicit should be from Y to X, as the goal was to have Y extend X, not to force a pre-existing object X to have additional functionality. Implicitly modifying an existing object would be much riskier than creating a new one that always inherits certain functionality via automatic conversions, provided those automatic conversions must always be imported together with the new object, so one doesn't have to consider whether the automatic conversion is in scope.Monopolize
There would be one big difference between that design and OO-inheritance, though...self-calls by X would still be to X, and not to overriding methods in Y.Monopolize
This is a smart and perfect answer to the question. Point! As for the "evil" and /or "never", never say "never", if not evil parrot. I found a nice use case for testing extensions to library code (you test it this way, and when ok you move the code to the lib). Thanks!Veradia
K
5

The only way to share code between two objects is by having one or more common superclass/trait.

Keister answered 2/10, 2011 at 7:47 Comment(1)
No, that change allows an object member of a superclass to be overriden by an object member in a subclass. class A { object a }; class B extends A { override object a }; ((new B): A).a // access B.aWildlife
E
5

Note that starting in Scala 3, you can alternatively use composition (instead of inheritance) via export clauses which allow defining aliases for selected members of an object:

object X { def f = 5 }

object Y {
  export X._
  def g = 42
  def h = f * g
}

Y.f // 5
Y.g // 42
Y.h // 210

Note that you can also restrict which members you want to export:

object X { def f = 5; def g = 6 }
object Y { export X.f }
Y.f // 5
Y.g
^^^
value g is not a member of Y
Emigrate answered 21/5, 2019 at 20:27 Comment(0)
B
2

You can convert parent into class + companion object, and then have child extend class E.g.

in Parent.scala

class Parent {}

object Parent extends Parent {}

And then in Child.scala

object Child extends Parent {}

Yes, it's more a hack than a solution.

Bettyebettzel answered 2/4, 2014 at 16:41 Comment(0)
A
2

This is a Scala 3 of Owen's answer: You can extend a companion object using extensions.

object X:
  def a = 5
end X

// Somewhere else, another file where X is visible
extension (x: X.type)
  def b = 42
end extension

// Somewhere else, another file where 
// both X and the extension are visible
@main def main =
  println(X.a)
  println(X.b) 
Alena answered 10/11, 2022 at 20:37 Comment(0)
S
0

The original question is 12 years old at the moment, now Scala is mature enough to have simple (much more simpler compared to other languages) answers to such requirements. Back to the original question, objects (more accurately singleton objects) are like values it's not meaningful to extend them, types are meant to support such scenarios. In Scala we have classes and traits for extension purposes so your wording should have been (i.e. extensibles should be declared as traits):

trait X
object Y extends X

On the other hand extension methods can be used to extend an object. Suppose we have:

object X:
  def oldMethod: Unit = ()
object Y

The following adds a new capability to Y based on old implementations from X (note that we should use the singleton type (Y.type) as the type of y, because Y itself is not a type but a specific and the only instance of that type):

extension (y: Y.type) def newMethod: Unit = X.oldMethod

Now the following successfully compiles:

Y.newMethod

To clarify, the original question is somehow ambiguous because it does not distinguish between values and types.

Scissor answered 20/10, 2023 at 11:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.