Scala: заменить метод во время выполнения

Допустим, у меня есть класс

class Original {
  def originalMethod = 1
}

Теперь, скажем, у меня есть экземпляр этого

val instance = new Original

Можно ли теперь что-то сделать с instance во время выполнения, чтобы заменить originalMethod другим методом? (при условии, что подпись остается прежней)

Например, теперь при вызове instance.originalMethod будет вызываться следующий код println("test"); 1

ИЗМЕНИТЬ Я не могу позвонить new Original. У меня просто есть существующий экземпляр, который я должен изменить.

EDIT 2 (@Aleksey Izmailov answer) Это хорошее решение, но это не совсем то, что я ищу. Я больше думаю в плане тестирования - написание класса "по-нормальному", без указания функций как переменных, а не методов

PS. Я наткнулся на этот вопрос, когда пытался имитировать шпиона Mockito.


person Anton    schedule 16.08.2015    source источник
comment
Возможно ли в вашем случае использовать агрегацию?   -  person maks    schedule 17.08.2015
comment
что вы имеете в виду под агрегацией?   -  person Anton    schedule 17.08.2015
comment
@Антон, ты нашел какое-нибудь решение этой проблемы? Я хотел бы имитировать поведение @MockBean или @SpyBean аннотаций среды Spring.   -  person scarface    schedule 06.07.2021
comment
@scarface да, ха-ха, я перестал программировать на Scala.   -  person Anton    schedule 08.07.2021


Ответы (2)


Это невозможно в JVM — ни в Java, ни в Scala — не так, как вы просите.

См., например. В Java с учетом объекта, можно ли переопределить один из методов?

Использование Scala вместо Java не дает вам дополнительных преимуществ, поскольку основы работы классов и методов в Scala такие же, как и в Java. (Это было сделано как из соображений производительности, так и из соображений взаимодействия.)

person Seth Tisue    schedule 19.08.2015

Похоже, вам нужно вернуться к мутации, поскольку у вас не может быть новых экземпляров, и поведение должно измениться. Вот, пожалуй, самый простой вариант (REPL):

Сохраните функцию как изменяемое поле:

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

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

Это функция, которая ничего не принимает, и вам нужно вызвать apply или (), чтобы получить результат.

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

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

Замените его новой функцией:

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

Теперь вы получаете новое поведение:

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

Интересно, что у вас не может быть реализации по умолчанию с универсальной версией этого класса, потому что нет значения по умолчанию, которое вы могли бы использовать, если вы не измените его на Unit или частичную функцию.

Вот его общая версия:

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
person yǝsʞǝla    schedule 17.08.2015
comment
Это хорошее решение, но это не совсем то, что я ищу. Я думаю больше с точки зрения тестирования - писать класс нормально, без указания функций как переменных, а не методов - person Anton; 17.08.2015
comment
Возможно, вы могли бы использовать макросы и переписать AST для того, что, по сути, будет делать то же самое, но скрывать детали от пользователя. - person yǝsʞǝla; 18.08.2015