2009-09-24 9 views
44

स्काला में, सबसे अच्छा तरीका है गतिशील रूप से एक वस्तु का दृष्टांत और प्रतिबिंब का उपयोग कर एक विधि आह्वान करने के लिए क्या है? ,स्काला: कैसे मैं गतिशील एक वस्तु का दृष्टांत और प्रतिबिंब का उपयोग कर एक विधि आह्वान करते हैं?

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

उपरोक्त कोड में दोनों वर्ग के नाम और विधि नाम गतिशील रूप में पारित कर रहे हैं:

मैं निम्नलिखित जावा कोड के स्काला बराबर करना चाहते हैं। ऊपर जावा तंत्र शायद Foo और hello() लिए इस्तेमाल किया जा सकता है, लेकिन स्काला प्रकार एक-से-एक जावा के साथ मेल नहीं खाते। उदाहरण के लिए, एक वर्ग एक सिंगलटन वस्तु के लिए परोक्ष घोषित किया जा सकता है। इसके अलावा स्कैला विधि सभी प्रकार के प्रतीकों का नाम होने की अनुमति देती है। दोनों नाम उलझन से हल हो जाते हैं। Interop Between Java and Scala देखें।

एक और मुद्दा यह भार के और autoboxing, Reflection from Scala - Heaven and Hell में वर्णित को दूर करने से मानकों के मिलान हो रहा है।

+2

यह देखते हुए कि मेरा उत्तर में प्रायोगिक सुविधा 2.8.0 नहीं किया, यह बेहतर होगा कि एक और जवाब के रूप में स्वीकार कर लिया चिह्नित किया गया था। –

+0

यदि मेरे पास क्लास मेलसेवरिस (ईमेल आईडी: स्ट्रिंग) जैसी कक्षा के पैरामीटर के साथ कक्षा है तो क्या रनटाइम पर गतिशील रूप से आह्वान करना संभव है? – ashK

उत्तर

57

वहाँ विधि आह्वान करने के लिए एक आसान तरीका है:

The only thing manifests give you now is the erasure of the static type of a parameter at the call site (contrary to getClass which give you the erasure of the dynamic type).


फिर आप प्रतिबिंब के माध्यम से एक विधि प्राप्त कर सकते हैं प्रतिबिंब तरीकों: संरचनात्मक टाइपिंग का उपयोग करें।

बस एक स्ट्रक्चरल प्रकार के ऑब्जेक्ट संदर्भ को डालें जिसमें आवश्यक विधि हस्ताक्षर है तो विधि को कॉल करें: कोई प्रतिबिंब आवश्यक नहीं है (बेशक, स्कैला नीचे प्रतिबिंब कर रहा है लेकिन हमें इसे करने की आवश्यकता नहीं है)।

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" 
    } 
} 
+11

स्ट्रक्चरल टाइप मेरी मदद नहीं करेगा अगर मुझे संकलन समय पर विधि का नाम नहीं पता है। संरचनात्मक प्रकार देने के लिए –

+7

एक प्रकार के उपनाम का उपयोग करते हुए एक नाम अक्सर इस चाल की पठनीयता में सुधार करता है। –

+1

यह केवल प्रश्न का हिस्सा जवाब देता है। विधि विधि = class.getMethod ("हैलो", शून्य) प्रदर्शन करने का एक स्काला केंद्रित तरीका है ?? –

6

instanciation हिस्सा Manifest इस्तेमाल कर सकते हैं: इस SO answer

experimental feature in Scala called manifests which are a way to get around a Java constraint regarding type erasure

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

With this version you still write

class Foo 
val t = new Test[Foo] 

However, if there's no no-arg constructor available you get a runtime exception instead of a static type error

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

तो सच प्रकार सुरक्षित समाधान को देखने के एक कारखाने का उपयोग करेंगे।


नोट: "केवल उपयोग एक कक्षा उदाहरण के रूप में प्रकार का विलोपन के लिए उपयोग दे रहा है।" के रूप में this thread में कहा गया है, प्रकट यहाँ रहने के लिए है, लेकिन अब के लिए है

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 = [email protected] 

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

VonC और Walter Chang द्वारा जवाब काफी अच्छा कर रहे हैं, इसलिए मैं सिर्फ एक स्काला 2.8 प्रायोगिक सुविधा के साथ पूरक होगा। वास्तव में, मैं भी, यह पोशाक के लिए परेशान नहीं करेगा मैं सिर्फ scaladoc कॉपी कर देंगे।

object Invocation 
    extends AnyRef 

A more convenient syntax for reflective invocation. Example usage:

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

You can call it reflectively one of two ways:

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. 

If you call the oo method and do not give the type inferencer enough help, it will most likely infer Nothing, which will result in a ClassCastException.

Author Paul Phillips

+0

यह लिंक प्रकार के ब्लॉग आलेख से मुझे दिमाग में था। पहेली का एक और टुकड़ा नाम मैंगलिंग सेवा है। –

+3

अफसोस की बात है कि, आमंत्रण इसे समाप्त नहीं कर पाया: https://lampsvn.epfl.ch/trac/scala/changeset/19695 –

3

मामले में आप एक स्काला 2.10 वस्तु (नहीं वर्ग) की एक विधि को लागू करने की जरूरत है और आप विधि और वस्तु के रूप में के नाम है String एस, आप इसे इस तरह से कर सकते हैं:

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 Documentation में और विवरण।

+1

किसी वर्ग के बारे में, कोई वस्तु नहीं? – matanster

4

@ 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" 
    ) 
) 
+1

संबंधित फॉलोअप: http://stackoverflow.com/questions/34227984/perfectly-handling-scala-constructors-in- गतिशील- स्थापना – matanster

संबंधित मुद्दे

 संबंधित मुद्दे