Оболочка универсального класса в scala

#scala

Вопрос:

Здравствуйте, я хотел бы создать универсальную оболочку в scala, чтобы отслеживать изменения значения любого типа. Я не знаю/до сих пор не нашел других способов, и я думал о создании класса, и я пытался использовать динамику, но у нее есть некоторые ограничения.

 case class Wrapper[T](value: T) extends Dynamic {
  private val valueClass = value.getClass

  def applyDynamic(id: String)(parameters: Any*) = {
    val objectParameters = parameters map (x => x.asInstanceOf[Object])
    val parameterClasses = objectParameters map (_.getClass)
    val method = valueClass.getMethod(id, parameterClasses:_*)
    val res = method.invoke(value, objectParameters:_*)

    // TODO: Logic that will eventually create some kind of event about the method invoked.

    new Wrapper(res)
  }
}
 

С этим кодом у меня возникают проблемы при вызове метода plus(» «) для двух целых чисел, и я не понимаю, почему. Разве в классе Int нет метода» «? Ошибка, которую я получаю, когда пытаюсь добавить как с типом оболочки/Int, так и с типом:

 var wrapped1 = Wrapper(1)
wrapped1 = wrapped1   Wrapper[2] // or just 2

type mismatch;
 found   : Wrapper[Int]/Int
 required: String
 

Почему он ожидает строку?

Если возможно, было бы также неплохо иметь возможность беспрепятственно работать как с оболочкой[T], так и с методами T, например

 val a = Wrapper[Int](1)
val b = Wrapper[Int](2)
val c = 3

a   b // Wrapper[Int]. (Wrapper[Int])
a   c // Wrapper[Int]. (Int)
c   a // Int. (Wrapper[Int]) 
 

Комментарии:

1. В конце концов вы столкнетесь с тем фактом, что примитивы распаковываются в Scala. Я написал 20-страничную статью о том, что именно вы пытаетесь сделать, и это только верхушка айсберга. В зависимости от того, как далеко вы хотите зайти, вы столкнетесь с миллионом различных проблем. Tl;dr это действительно невозможно в полном объеме без написания плагина компилятора, чтобы явно изменить то, как работают определенные вещи.

Ответ №1:

Также, если вы хотите сделать прокси-сервер, который будет получать никаких изменений нужные значения, вы, вероятно, не без агентов(https://dzone.com/articles/java-agent-1), потому что это заставит вас сделать код модификации байт-кода, который принимает окончательное классы и примитивы, чтобы принять ваш прокси вместо этого, и это потребует более перехватив изменения «просто класс», но и все классы членов жюри и произвести происхождения-стоимостного анализа и так далее. Это отнюдь не тривиальная проблема.

Другой подход состоит в том, чтобы создавать различия классов вариантов путем сравнения классов в определенных точках выполнения, и есть такая общая реализация, она использует вывод для вычисления различий: https://github.com/ivan71kmayshan27/ShapelesDerivationExample Я верю, что вы можете найти более простое решение с магнолией. На самом деле это не может работать только для классов, если вы не напишете свой собственный макрос и у вас не возникнут некоторые проблемы с упорядоченными и неупорядоченными коллекциями.

Комментарии:

1. Первое не сработает. API метапрограммирования Java сталкивается со многими вещами в Scala. Даже в Java прокси работают только с интерфейсами, а не с конкретными классами. Я не совсем понимаю, что вы имели в виду под второй частью. Я понимаю, что ОП хочет перехватить вызовы методов, чтобы понять, как развивается значение. Самый простой пример заключается в том, что это не принесет никакой пользы примитивам. Или я неправильно понял ваше замечание?

2. Первое будет работать, потому что код scala преобразуется в байт-код, как и все остальное, и именно поэтому вы можете использовать код scala как из kotlin, так и из java, таким образом, вы можете изменить его с помощью API, который позволяет процессорам байт-кода. Вам все равно придется учитывать некоторые вещи в scala, но говорить «это вообще не сработает» в основном неверно. Второй подход заключается в использовании простых различий в ценностях, а не в шпионаже.

3. Не совсем верно для Scala 3, но да, это выходит за рамки этого вопроса. Я просто уходил от связанной статьи. Я просто пытался сказать, что представленная там методология не может охватить все конструкции Scala. Есть способы сделать это, но это очень быстро выходит из-под контроля.