我有一个scala对象,如下所示:
package com.example
object Foo {
def bar(): String = "Interesting Result"
}
我知道如果Foo$.MODULE$.bar()
在构建和运行时类路径中,我可以从Java调用Foo
,但在我的情况下Foo
不在构建类路径中,可能会也可能不会在运行时类路径中配置。
从我的Java代码中,我想使用反射调用bar()
,如果它在运行时类路径上可用,否则我将回退到默认实现。
是否可以这样做?
答案 0 :(得分: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 :(得分: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
// ...
}
答案 2 :(得分:1)
受David Carlson answer的启发,我创建了这个实用程序类,用于反射调用Scala对象上的方法。 该类还允许您调用期望参数的Scala方法。
我把它放在这里,希望它对某些人有用,并获得有关如何改进代码的反馈。
ScalaUtil.java:
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() {
}
}
为了使用参数调用Scala方法,您需要将Scala库添加到Java项目的构建路径中。
假设这是您要调用的Scala对象:
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的方法:
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");