Scala : 객체를 동적으로 인스턴스화하고 반사를 사용하여 방법을 호출하는 방법은 무엇입니까?
-
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 객체 (클래스가 아님)의 메소드를 호출 해야하는 경우 메소드와 객체의 이름이 다음과 같습니다. String
s, 당신은 다음과 같이 할 수 있습니다.
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"
)
)