2011-06-30 12 views
8

私は次のように定義されたScalaのオブジェクトを持っている:JavaからScalaオブジェクトのメソッドを反射的に呼び出すにはどうすればよいですか?

package com.example 

object Foo { 
    def bar(): String = "Interesting Result" 
} 

私はFooビルドと実行時クラスパスにありますが、私の状況でFooは、ビルドのクラスパスではなく、場合、私は、JavaからFoo$.MODULE$.bar()を呼び出すことができることを知っています実行時クラスパスで構成されていてもいなくても構いません。

私のJavaコードから、リフレクションを使用してbar()を呼び出してください(ランタイムクラスパスで使用可能な場合)。そうでなければ、デフォルトの実装に戻ります。

これは可能ですか?あなたはこのようなものなコードでそれを行うことができます

答えて

5

package com.example.java; 

import java.lang.reflect.Field; 
import java.lang.reflect.InvocationTargetException; 
import java.lang.reflect.Method; 

public class Example { 

    /** 
    * Returns null or the result of calling a method on a scala object from java 
    */ 
    public String callScalaFromJava(){ 
    String result = null; 

    try { 
     Class<?> clazz = Class.forName("com.example.Foo$"); // Note the trailing '$' 
     Method method = clazz.getDeclaredMethod("bar"); 
     Field field = clazz.getField("MODULE$"); 
     Object instance = field.get(null); 
     Object obj = method.invoke(instance, new Object[] {}); 

     if (obj instanceof String) { 
     result = (String) obj); 
     } 

    } catch (Exception ex) { 
     // SWALLOWING 
    } 
    return result; 
    } 
} 
+1

反射のための必要はありません。

package eu.tests.scala object ScalaObject { // You'll have to use Java's boolean when Java calls this def sayHello(param: java.lang.Boolean): String = "param: " + param def sayHello: String = "no param" def sayHello(param: String): String = "param: " + param } 

これは、あなたが上記のScalaのオブジェクトにScalaUtilを使用する方法である。このことを仮定し

は、あなたが呼び出したいScalaのオブジェクトです。コードは、 'Foo.bar()'または 'Foo $ .MODULE $ .bar()'のいずれかを介して直接アクセスできます。 – sschaef

2

オブジェクトのFooクラスを使用すると、ちょうどそのクラスをロードすることができれば、すべてがリフレクションを使用せずに罰金行くのでcom.example.Foo$です:

try { 
    Class.forName("com.example.Foo$"); 
    String s = com.example.Foo$.MODULE$.bar(); 
    // ... 
} catch (Exception ex) { 
    String s = // fallback solution 
    // ... 
} 
1

デイビッド・カールソンの触発answer私はScalaオブジェクトのメソッドを反映するために、このユーティリティクラスを作成しました。 クラスを使用すると、パラメータを必要とするScalaメソッドも呼び出すことができます。

コードを改善するためのフィードバックをお寄せいただき、ありがとうございます。

ScalaUtil.java:あなたはJavaプロジェクトのビルド・パスにScalaのライブラリを追加する必要がありますパラメータでScalaのメソッドを呼び出すために

package your.stuff.utils; 

import java.lang.reflect.Field; 
import java.lang.reflect.InvocationTargetException; 
import java.lang.reflect.Method; 
// I use SLF4J (http://www.slf4j.org/) for logging. Feel free to change this 
import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 

/** Utilities for Scala/Java integration */ 
public class ScalaUtil { 

    private static final Logger LOG = LoggerFactory.getLogger(ScalaUtil.class); 

    /** 
    * Calls the parameterless {@code method} on a Scala {@code object}. 
    * <p> 
    * Returns an object of type {@code ReturnType} if the call succeeded, null 
    * otherwise. 
    * 
    * @param object 
    *   the fully qualified path to the object such as 
    *   "eu.test.MyScalaObj$". Mind the dollar sign at the end 
    * @param method 
    *   the name of the method inside {@code object} we want to call 
    * @param <ReturnType> 
    *   type of the return value of the {@code method} 
    * @return the return value of the Scala {@code object}'s {@code method} or 
    *   null if the {@code method} couldn't be called 
    */ 
    public static <ReturnType> ReturnType callObj(final String object, 
      final String method) { 
     final Object noParams[] = {}; 
     return callObj(object, method, noParams); 
    } 

    /** 
    * Calls a {@code method} on a Scala {@code object} with the given method 
    * {@code arguments}. 
    * <p> 
    * Returns an object of type {@code ReturnType} if the call succeeded, null 
    * otherwise. 
    * 
    * @param object 
    *   the fully qualified path to the object such as 
    *   "eu.test.MyScalaObj$". Mind the dollar sign at the end 
    * @param method 
    *   the name of the method inside {@code object} we want to call 
    * @param arguments 
    *   the arguments that {@code method} expects 
    * @param <ReturnType> 
    *   type of the return value of the {@code method} 
    * @return the return value of the Scala {@code object}'s {@code method} or 
    *   null if the {@code method} couldn't be called 
    */ 
    @SuppressWarnings("unchecked") 
    public static <ReturnType> ReturnType callObj(final String object, 
      final String method, final Object... arguments) { 
     ReturnType result = null; 
     Class<?> objClass = null; 
     try { 
      final Class<?>[] methArgTypes = getTypes(arguments); 
      objClass = Class.forName(object); 
      final Method meth = objClass 
        .getDeclaredMethod(method, methArgTypes); 
      final Field field = objClass.getField("MODULE$"); 
      final Object instance = field.get(null); 
      result = (ReturnType) meth.invoke(instance, arguments); 
     } catch (ClassNotFoundException | SecurityException 
       | NoSuchFieldException | IllegalAccessException 
       | IllegalArgumentException | InvocationTargetException e) { 
      LOG.warn("Could not call method {} on {} with arguments {}", 
        method, object, arguments, e); 
     } catch (final NoSuchMethodException e) { 
      if (objClass != null) { 
       LOG.info("Declared methods: {}", 
         (Object[]) objClass.getDeclaredMethods()); 
      } 
      LOG.warn("Could not call method {} on {} with arguments {}", 
        method, object, arguments, e); 
     } 
     return result; 
    } 

    /** 
    * Returns the runtime types of some {@code objects}. 
    * 
    * @param objects 
    *   the objects in whose types we are interested in 
    * @return an array of the runtime types of the {@code objects} 
    */ 
    private static Class<?>[] getTypes(final Object... objects) { 
     final Class<?>[] types = new Class<?>[objects.length]; 
     for (int i = 0; i < objects.length; i++) { 
      final Object o = objects[i]; 
      final Class<?> type = o.getClass(); 
      types[i] = type; 
     } 
     return types; 
    } 

    /** This utility class is not meant to be instantiated */ 
    private ScalaUtil() { 
    } 
} 

String scalaPackage = "eu.tests.scala"; 
String scalaObject = "ScalaObject"; 
String method = "sayHello"; 
String fullScalaObjName = scalaPackage + "." + scalaObject + "$"; 

String result1 = ScalaUtil.callObj(fullScalaObjName, method); 
String result2 = ScalaUtil.callObj(fullScalaObjName, method, true); 
String result3 = ScalaUtil.callObj(fullScalaObjName, method, "abc"); 
関連する問題