4

Let's say I have a class

class Original {
  def originalMethod = 1
}

Now, let's say I have an instance of it

val instance = new Original

Is it now possible to do something to instance at runtime to replace the originalMethod with a different method? (granted the signature stays the same)

For example, now, when calling instance.originalMethod the following code will be called println("test"); 1

EDIT I can't call new Original. I just have an existing instance of it, which I have to modify.

EDIT 2 (@Aleksey Izmailov answer) This is a nice solution, but isn't exactly what I'm looking for. I'm thinking more in terms of testing - writing a class "normally", without specifying functions as variables rather than methods

PS. I stumbled on this question when trying to mimic a Mockito spy

Anton
  • 2,282
  • 26
  • 43

2 Answers2

2

Seems like you have to revert to mutation since you can't have new instances and behavior has to change. Here is perhaps the simplest option (REPL):

Store a function as a mutable field:

scala> class Original {
     |   var originalMethod = () => 1
     | }
defined class Original

scala> val obj = new Original
obj: Original = Original@66ac5762

It's a function that takes nothing, and you need to call apply or () on it to get result.

scala> obj.originalMethod
res0: () => Int = <function0>

scala> obj.originalMethod()
res1: Int = 1                       ^

Replace it with a new function:

scala> obj.originalMethod = () => 2
obj.originalMethod: () => Int = <function0>

Now you are getting new behaviour:

scala> obj.originalMethod()
res2: Int = 2

Interestingly enough you can't have default implementation with a generic version of this class because there is no default value you could use unless you change it to Unit or partial function.

Here is a generic version of it:

scala> class Original[T] {                                                               
     |   var originalMethod: () => T = null                                              
     | }                                                                                 
defined class Original                                                                   

scala> val genImpl = new Original[Int] { originalMethod = () => 111 }                    
genImpl: Original[Int] = $anon$1@6b04acb2                                                       

scala> genImpl.originalMethod()                                                          
res8: Int = 111                                                                          

scala> genImpl.originalMethod = () => 222
genImpl.originalMethod: () => Int = <function0>

scala> genImpl.originalMethod()
res9: Int = 222
yǝsʞǝla
  • 16,272
  • 2
  • 44
  • 65
  • This is a nice solution, but isn't exactly what I'm looking for. I'm thinking more in terms of testing - writing a class "normally", without specifying functions as variables rather than methods – Anton Aug 17 '15 at 19:14
  • Maybe you could use macros and rewrite ASTs for that which essentially would do the same thing but hide details away from the user. – yǝsʞǝla Aug 18 '15 at 02:25
1

This isn't possible on the JVM — in either Java or Scala — not in the way that you're asking for.

See e.g. In Java, given an object, is it possible to override one of the methods?

Being in Scala instead of Java doesn't gain you additional leverage, since the fundamentals of how classes and methods work in Scala are the same as Java's. (This was done for both performance and interop reasons.)

Community
  • 1
  • 1
Seth Tisue
  • 29,985
  • 11
  • 82
  • 149