Scala : 객체를 동적으로 인스턴스화하고 반사를 사용하여 방법을 호출하는 방법은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/1469958

  •  16-09-2019
  •  | 
  •  

문제

Scala에서 객체를 동적으로 인스턴스화하고 반사를 사용하여 방법을 호출하는 가장 좋은 방법은 무엇입니까?

다음 Java 코드의 Scala와 동등한 일을하고 싶습니다.

Class class = Class.forName("Foo");
Object foo = class.newInstance();
Method method = class.getMethod("hello", null);
method.invoke(foo, null);

위 코드에서 클래스 이름과 메소드 이름이 동적으로 전달됩니다. 위의 Java 메커니즘이 사용될 수 있습니다 Foo 그리고 hello(), 그러나 스칼라 유형은 자바와 일대일 일치하지 않습니다. 예를 들어, 클래스는 싱글 톤 객체에 대해 암시 적으로 선언 될 수 있습니다. 또한 스칼라 방법을 사용하면 모든 종류의 기호가 이름이 될 수 있습니다. 둘 다 이름 Mangling으로 해결됩니다. 보다 Java와 Scala 사이의 인터 로프.

또 다른 문제는 과부하를 해결하고 Autoboxing을 해결함으로써 매개 변수의 일치하는 것 같습니다. Scala의 반성 - 천국과 지옥.

도움이 되었습니까?

해결책

Java 반사 방법을 호출하지 않고 반사적으로 방법을 호출하는 쉬운 방법이 있습니다. 구조 타이핑을 사용하십시오.

필요한 메소드 서명이있는 구조 유형에 대한 객체 참조를 시전 한 다음 방법을 호출 할 필요가 없습니다 (물론 Scala는 아래에 반사를하고 있지만 수행 할 필요는 없습니다).

class Foo {
  def hello(name: String): String = "Hello there, %s".format(name)
}

object FooMain {

  def main(args: Array[String]) {
    val foo  = Class.forName("Foo").newInstance.asInstanceOf[{ def hello(name: String): String }]
    println(foo.hello("Walter")) // prints "Hello there, Walter"
  }
}

다른 팁

답변 그리고 월터 창 꽤 좋으므로 하나의 Scala 2.8 실험 기능을 보완하겠습니다. 사실, 나는 옷을 입히는 것을 귀찮게하지 않을 것입니다. 나는 단지 scaladoc을 복사 할 것입니다.

object Invocation
  extends AnyRef

반사 호출을위한보다 편리한 구문. 예제 사용 :

class Obj { private def foo(x: Int, y: String): Long = x + y.length }

두 가지 방법 중 하나를 반사적으로 부를 수 있습니다.

import scala.reflect.Invocation._
(new Obj) o 'foo(5, "abc")                 // the 'o' method returns Any
val x: Long = (new Obj) oo 'foo(5, "abc")  // the 'oo' method casts to expected type.

OO 메소드를 호출하고 유형 추론 자에게 충분한 도움을주지 않으면 아무것도 추론 할 수 없을 것입니다.

저자 폴 필립스

Scala 2.10 객체 (클래스가 아님)의 메소드를 호출 해야하는 경우 메소드와 객체의 이름이 다음과 같습니다. Strings, 당신은 다음과 같이 할 수 있습니다.

package com.example.mytest

import scala.reflect.runtime.universe

class MyTest

object MyTest {

  def target(i: Int) = println(i)

  def invoker(objectName: String, methodName: String, arg: Any) = {
    val runtimeMirror = universe.runtimeMirror(getClass.getClassLoader)
    val moduleSymbol = runtimeMirror.moduleSymbol(
      Class.forName(objectName))

    val targetMethod = moduleSymbol.typeSignature
      .members
      .filter(x => x.isMethod && x.name.toString == methodName)
      .head
      .asMethod

    runtimeMirror.reflect(runtimeMirror.reflectModule(moduleSymbol).instance)
      .reflectMethod(targetMethod)(arg)
  }

  def main(args: Array[String]): Unit = {
    invoker("com.example.mytest.MyTest$", "target", 5)
  }
}

이 지문 5 표준 출력으로. 자세한 내용 스칼라 문서.

인스턴케이션 부분 ~할 수 있었다 사용 명백한: 이것 좀 봐 그러니 대답

Scala의 실험적 특징은 유형 삭제에 관한 Java 제약을 얻는 방법입니다.

 class Test[T](implicit m : Manifest[T]) {
   val testVal = m.erasure.newInstance().asInstanceOf[T]
 }

이 버전을 사용하면 여전히 씁니다

class Foo
val t = new Test[Foo]

그러나 사용 가능한 ARG 생성자가 없으면 정적 유형 오류 대신 런타임 예외가 발생합니다.

scala> new Test[Set[String]] 
java.lang.InstantiationException: scala.collection.immutable.Set
at java.lang.Class.newInstance0(Class.java:340)

따라서 진정한 타입 안전 솔루션은 공장을 사용하는 것입니다.


참고 : 언급 한 바와 같이 이 스레드, Manifest는 여기에 머무르기 위해 여기에 있지만, 현재는 "클래스 인스턴스로서 유형의 소거에 접근하는 것만 사용하는 것"입니다.

지금 당신에게 나타나는 유일한 것은 공전 통화 사이트의 매개 변수 유형 ( getClass 당신에게 당신에게의 소거를줍니다 동적 유형).


그런 다음 반사를 통해 메소드를 얻을 수 있습니다.

classOf[ClassName].getMethod("main", classOf[Array[String]]) 

그리고 그것을 불러냅니다

scala> class A {
     | def foo_=(foo: Boolean) = "bar"
     | }
defined class A

scala>val a = new A
a: A = A@1f854bd

scala>a.getClass.getMethod(decode("foo_="),
classOf[Boolean]).invoke(a, java.lang.Boolean.TRUE)
res15: java.lang.Object = bar 

@nedim의 답변에서 일하면서 여기에 기초 완전한 답변을 위해, 여기에 주요 차이가 순진한 수업을 인스턴스화합니다. 이 코드는 여러 생성자의 경우를 처리하지 않으며 결코 완전한 답이 아닙니다.

import scala.reflect.runtime.universe

case class Case(foo: Int) {
  println("Case Case Instantiated")
}

class Class {
  println("Class Instantiated")
}

object Inst {

  def apply(className: String, arg: Any) = {
    val runtimeMirror: universe.Mirror = universe.runtimeMirror(getClass.getClassLoader)

    val classSymbol: universe.ClassSymbol = runtimeMirror.classSymbol(Class.forName(className))

    val classMirror: universe.ClassMirror = runtimeMirror.reflectClass(classSymbol)

    if (classSymbol.companion.toString() == "<none>") // TODO: use nicer method "hiding" in the api?
    {
      println(s"Info: $className has no companion object")
      val constructors = classSymbol.typeSignature.members.filter(_.isConstructor).toList
      if (constructors.length > 1) { 
        println(s"Info: $className has several constructors")
      } 
      else {
        val constructorMirror = classMirror.reflectConstructor(constructors.head.asMethod) // we can reuse it
        constructorMirror()
      }

    }
    else
    {
      val companionSymbol = classSymbol.companion
      println(s"Info: $className has companion object $companionSymbol")
      // TBD
    }

  }
}

object app extends App {
  val c = Inst("Class", "")
  val cc = Inst("Case", "")
}

여기에 있습니다 build.sbt 그것은 그것을 컴파일 할 것입니다 :

lazy val reflection = (project in file("."))
  .settings(
    scalaVersion := "2.11.7",
    libraryDependencies ++= Seq(
      "org.scala-lang" % "scala-compiler" % scalaVersion.value % "provided",
      "org.scala-lang" % "scala-library" % scalaVersion.value % "provided"
    )
  )
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top