Отражение Scala для преобразования с использованием возвращаемого типа метода во время выполнения

Я использую отражение в scala, и я хочу обобщить код для приведения объекта для возврата типа другого метода, который идентифицируется во время выполнения с использованием отражения

Я попытался получить возвращаемый тип метода с помощью отражения, но не смог использовать это в операции приведения asInstance [].

import java.lang.reflect.Modifier
object Test5 {

  def main(args: Array[String]): Unit = {
    val runtimeClass = Class.forName("ConvertToUpper")
    invokeRuntimeMethod(runtimeClass, "toUpper")
  }

  def invokeRuntimeMethod(runtimeClass: Class[_], methodName: String): Unit = {
    val runtimeMethod = runtimeClass.getDeclaredMethod(methodName)
    var runtimeClsConstructor: java.lang.reflect.Constructor[_] = null
    if (!Modifier.isStatic(runtimeMethod.getModifiers)) {
      runtimeClsConstructor = runtimeClass.getDeclaredConstructor()
      runtimeClsConstructor.setAccessible(true)
    }
    println("Return Type =>" + runtimeMethod.getReturnType)
    println("Generic Return Type => " + runtimeMethod.getGenericReturnType)

    runtimeMethod.setAccessible(true)
    val runtimeObj = if (runtimeClsConstructor != null) runtimeClsConstructor.newInstance()
    else runtimeClsConstructor

    val runtimeFunction = runtimeMethod.invoke(runtimeObj).asInstanceOf[Function1[String, String]]
    println("output => " + runtimeFunction("test"))

  }
}

Здесь я хочу обобщить функцию, чтобы мне не нужно было писать Function1. Я уже получаю возвращаемый тип, то есть Generic Return Type => scala.Function1. Как я могу использовать этот возвращаемый тип в asInstanceOf напрямую вместо жесткого кодирования Function1, например runtimeMethod.invoke (runtimeObj) .asInstanceOf [runtimeMethod.getGenericReturnType]


person Hemendra Yadav    schedule 25.07.2019    source источник
comment
Если тип взят из среды выполнения, как бы вы его использовали? Вам либо нужна эта информация, полученная другим способом во время компиляции. У нас вы получите что-то вроде Any => Any. - Могу я спросить, каков ваш вариант использования? Каким должен быть общий? И зачем вам вообще понадобилась рефлексия?   -  person Luis Miguel Mejía Suárez    schedule 25.07.2019
comment
Мой вариант использования - предоставить тип runtimeFunction для метода spark (spark.udf.register), чтобы функция udf могла быть вызвана с необходимым количеством аргументов, таких как spark.udf.register (toUpper, runtimeFunction)   -  person Hemendra Yadav    schedule 25.07.2019
comment
Но тогда будет ли имя метода появиться во время выполнения? Затем, даже если вы могли бы извлекать аргументы и тип возвращаемого значения динамически, вам нужно будет распространить всю эту информацию туда, где используется UDF, и все эти значения также должны поступать из среды выполнения? Или, если вы используете это во время компиляции, вам нужно будет предоставить информацию о типе статически. - В любом случае, в первом случае кажется, что вы будете в основном программировать во время выполнения, а во втором кажется, что отражение на самом деле вообще не нужно. - Я бы порекомендовал вам переосмыслить то, что вы пытаетесь делать.   -  person Luis Miguel Mejía Suárez    schedule 25.07.2019
comment
Отражение во время выполнения обычно является симптомом проблемы с кодом / дизайном   -  person cchantep    schedule 25.07.2019
comment
Это может быть проблема X-Y (то есть может быть другой способ достичь того, что вам нужно делать без отражения, чего обычно избегают в Scalaland). Что тебе действительно нужно делать?   -  person Thilo    schedule 27.07.2019


Ответы (1)


Я уже получаю возвращаемый тип, то есть Generic Return Type => scala.Function1.

Вы знаете это во время выполнения.

Как я могу использовать этот возвращаемый тип в asInstanceOf напрямую вместо жесткого кодирования Function1

Для этого вы должны знать во время компиляции, что это Function1.

Итак, вопрос в том, знаете ли вы во время компиляции, что метод ConvertToUpper#toUpper возвращает Function1, или это может быть что угодно.

Если да, то вы можете попытаться привести к Function1[Any, Any] (если во время компиляции не известно, что аргументы равны Strings)

val runtimeFunction = runtimeMethod.invoke(runtimeObj).asInstanceOf[Function1[Any, Any]]

Если нет, то вы не можете знать, что можете вызвать apply в runtimeFunction("test") (он же runtimeFunction.apply("test")).


Если единственная проблема заключается в том, что функция может быть Function1, _11 _..., тогда вы должны сопоставить

runtimeMethod.invoke(runtimeObj) match { 
  case _ : Function1[_, _] => ???
  case _ : Function2[_, _, _] => ???
  ... 
}
person Dmytro Mitin    schedule 26.07.2019
comment
Ниже приводится более подробная информация о моем варианте использования: 1) Первый пользователь подготовит udf, как будто он создает класс ConvertToUpper с методом toUpper. 2) Теперь пользователь проходит над udf jar в пути к классу spark и предоставляет полное имя метода, например ConvertToUpper.toUpper, для регистра udf искры. 3) Наряду с вышесказанным он предоставит запрос вроде select toUpper (col1) from test; Здесь я хочу, чтобы регистр искр udf был общим, я не хочу ставить проверки, например, если пользовательская функция имеет тип Function1, Function2 или Function3. - person Hemendra Yadav; 26.07.2019
comment
@HemendraYadav, если единственная проблема в том, что функция может быть Function1, _2 _... тогда вы должны сопоставить runtimeMethod.invoke(runtimeObj) match { case _ : Function1[_, _] => ???; case _ : Function2[_, _, _] => ???; ... } - person Dmytro Mitin; 26.07.2019
comment
Здесь 22 функции {1 ... 22}, а также 22 UDF {1 .... 22} и другие типы. Я не хочу ставить чек на всех до единого. - person Hemendra Yadav; 26.07.2019
comment
@HemendraYadav, поэтому люди создают код в scala github .com / milessabin / shapeless / blob / master / project / github.com/scala/scala/blob/2.13.x/project/ - person Dmytro Mitin; 26.07.2019