我正在寻找一种通过引用传递方法的方法。我知道 Java 不会将方法作为参数传递,但是,我想找一个替代方案。
有人告诉我,接口是作为参数传递方法的替代方法,但我不明白接口如何通过引用充当方法。如果我理解正确,接口只是一组未定义的抽象方法。我不想发送一个每次都需要定义的接口,因为几个不同的方法可以使用相同的参数调用相同的方法。
我想要完成的是与此类似的事情:
public void setAllComponents(Component[] myComponentArray, Method myMethod) {
for (Component leaf : myComponentArray) {
if (leaf instanceof Container) { //recursive call if Container
Container node = (Container) leaf;
setAllComponents(node.getComponents(), myMethod);
} //end if node
myMethod(leaf);
} //end looping through components
}
调用如:
setAllComponents(this.getComponents(), changeColor());
setAllComponents(this.getComponents(), changeSize());
编辑:从 Java 8 开始,lambda expressions 是一个很好的解决方案,正如 other answers 指出的那样。下面的答案是为 Java 7 及更早版本编写的......
看看 command pattern。
// NOTE: code not tested, but I believe this is valid java...
public class CommandExample
{
public interface Command
{
public void execute(Object data);
}
public class PrintCommand implements Command
{
public void execute(Object data)
{
System.out.println(data.toString());
}
}
public static void callCommand(Command command, Object data)
{
command.execute(data);
}
public static void main(String... args)
{
callCommand(new PrintCommand(), "hello world");
}
}
编辑: 作为 Pete Kirkham points out,还有另一种使用 Visitor 的方法。访问者方法涉及更多一些 - 您的节点都需要使用 acceptVisitor()
方法感知访问者 - 但如果您需要遍历更复杂的对象图,那么值得研究。
在 Java 8 中,您现在可以使用 Lambda Expressions 和方法引用更轻松地传递方法。首先,一些背景知识:函数式接口是一个只有一个抽象方法的接口,尽管它可以包含任意数量的 default methods(Java 8 中的新方法)和静态方法。 lambda 表达式可以快速实现抽象方法,如果您不使用 lambda 表达式,则不需要所有不必要的语法。
没有 lambda 表达式:
obj.aMethod(new AFunctionalInterface() {
@Override
public boolean anotherMethod(int i)
{
return i == 982
}
});
使用 lambda 表达式:
obj.aMethod(i -> i == 982);
以下是 the Java tutorial on Lambda Expressions 的摘录:
Lambda 表达式的语法 lambda 表达式包含以下内容: 括号中的形式参数的逗号分隔列表。 CheckPerson.test 方法包含一个参数 p,它表示 Person 类的一个实例。注意:您可以省略 lambda 表达式中参数的数据类型。此外,如果只有一个参数,您可以省略括号。例如,以下 lambda 表达式也是有效的: p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25 箭头标记,->主体,由单个表达式或语句块组成。此示例使用以下表达式: p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25 如果指定单个表达式,则 Java 运行时计算表达式,然后返回它的值。或者,您可以使用 return 语句: p -> { return p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25; } return 语句不是表达式;在 lambda 表达式中,您必须将语句括在大括号 ({}) 中。但是,您不必将 void 方法调用括在大括号中。例如,以下是一个有效的 lambda 表达式: email -> System.out.println(email) 注意 lambda 表达式看起来很像方法声明;您可以将 lambda 表达式视为匿名方法——没有名称的方法。
以下是使用 lambda 表达式“传递方法”的方法:
interface I {
public void myMethod(Component component);
}
class A {
public void changeColor(Component component) {
// code here
}
public void changeSize(Component component) {
// code here
}
}
class B {
public void setAllComponents(Component[] myComponentArray, I myMethodsInterface) {
for(Component leaf : myComponentArray) {
if(leaf instanceof Container) { // recursive call if Container
Container node = (Container)leaf;
setAllComponents(node.getComponents(), myMethodInterface);
} // end if node
myMethodsInterface.myMethod(leaf);
} // end looping through components
}
}
class C {
A a = new A();
B b = new B();
public C() {
b.setAllComponents(this.getComponents(), component -> a.changeColor(component));
b.setAllComponents(this.getComponents(), component -> a.changeSize(component));
}
}
通过使用方法引用可以进一步缩短类 C
,如下所示:
class C {
A a = new A();
B b = new B();
public C() {
b.setAllComponents(this.getComponents(), a::changeColor);
b.setAllComponents(this.getComponents(), a::changeSize);
}
}
::
运算符),否则 A 是什么并不重要。 a.changeThing(component)
可以更改为您想要的任何语句或代码块,只要它返回 void。
从 Java 8 开始有一个 Function<T, R>
接口 (docs),它有方法
R apply(T t);
您可以使用它将函数作为参数传递给其他函数。 T 是函数的输入类型,R 是返回类型。
在您的示例中,您需要传递一个将 Component
类型作为输入且不返回任何内容的函数 - Void
。在这种情况下,Function<T, R>
不是最佳选择,因为没有 Void 类型的自动装箱。您正在寻找的接口称为 Consumer<T>
(docs) 与方法
void accept(T t);
它看起来像这样:
public void setAllComponents(Component[] myComponentArray, Consumer<Component> myMethod) {
for (Component leaf : myComponentArray) {
if (leaf instanceof Container) {
Container node = (Container) leaf;
setAllComponents(node.getComponents(), myMethod);
}
myMethod.accept(leaf);
}
}
你可以使用方法引用来调用它:
setAllComponents(this.getComponents(), this::changeColor);
setAllComponents(this.getComponents(), this::changeSize);
假设您在同一个类中定义了 changeColor() 和 changeSize() 方法。
如果您的方法碰巧接受多个参数,您可以使用 BiFunction<T, U, R>
- T 和 U 是输入参数类型,R 是返回类型。还有 BiConsumer<T, U>
(两个参数,没有返回类型)。不幸的是,对于 3 个或更多的输入参数,您必须自己创建一个接口。例如:
public interface Function4<A, B, C, D, R> {
R apply(A a, B b, C c, D d);
}
使用 java.lang.reflect.Method
对象并调用 invoke
首先定义一个接口,使用你想要作为参数传递的方法
public interface Callable {
public void call(int param);
}
用方法实现一个类
class Test implements Callable {
public void call(int param) {
System.out.println( param );
}
}
// 像这样调用
Callable cmd = new Test();
这允许您将 cmd 作为参数传递并调用接口中定义的方法调用
public invoke( Callable callable ) {
callable.call( 5 );
}
虽然这对 Java 7 及更低版本还无效,但我相信我们应该展望未来并至少认识到 the changes 会出现在 Java 8 等新版本中。
也就是说,这个新版本为 Java 带来了 lambdas 和方法引用(以及 new APIs,这是解决此问题的另一个有效解决方案。虽然它们仍然需要一个接口,但不会创建新对象,并且额外的类文件不需要污染输出目录由于JVM的不同处理。
两种风格(lambda 和方法引用)都需要一个接口,该接口可用于使用签名的单个方法:
public interface NewVersionTest{
String returnAString(Object oIn, String str);
}
从这里开始,方法的名称将不再重要。在接受 lambda 的情况下,方法引用也是如此。例如,在这里使用我们的签名:
public static void printOutput(NewVersionTest t, Object o, String s){
System.out.println(t.returnAString(o, s));
}
这只是一个简单的接口调用,直到 lambda1 被传递:
public static void main(String[] args){
printOutput( (Object oIn, String sIn) -> {
System.out.println("Lambda reached!");
return "lambda return";
}
);
}
这将输出:
Lambda reached!
lambda return
方法参考类似。鉴于:
public class HelperClass{
public static String testOtherSig(Object o, String s){
return "real static method";
}
}
主要:
public static void main(String[] args){
printOutput(HelperClass::testOtherSig);
}
输出将是 real static method
。 Method references can be static, instance, non-static with arbitrary instances, and even constructors。对于构造函数,将使用类似于 ClassName::new
的东西。
1 这不被某些人认为是 lambda,因为它有副作用。然而,它确实说明了以更直观的方式使用一个。
上次我检查时,Java 无法在本机上做你想做的事;您必须使用“解决方法”来解决这些限制。在我看来,接口是一种选择,但不是一个好的选择。也许谁告诉你的意思是这样的:
public interface ComponentMethod {
public abstract void PerfromMethod(Container c);
}
public class ChangeColor implements ComponentMethod {
@Override
public void PerfromMethod(Container c) {
// do color change stuff
}
}
public class ChangeSize implements ComponentMethod {
@Override
public void PerfromMethod(Container c) {
// do color change stuff
}
}
public void setAllComponents(Component[] myComponentArray, ComponentMethod myMethod) {
for (Component leaf : myComponentArray) {
if (leaf instanceof Container) { //recursive call if Container
Container node = (Container) leaf;
setAllComponents(node.getComponents(), myMethod);
} //end if node
myMethod.PerfromMethod(leaf);
} //end looping through components
}
然后你会调用它:
setAllComponents(this.getComponents(), new ChangeColor());
setAllComponents(this.getComponents(), new ChangeSize());
如果您不需要这些方法返回某些东西,您可以让它们返回 Runnable 对象。
private Runnable methodName (final int arg) {
return (new Runnable() {
public void run() {
// do stuff with arg
}
});
}
然后像这样使用它:
private void otherMethodName (Runnable arg){
arg.run();
}
我没有找到任何对我来说足够明确的示例来说明如何将 java.util.function.Function
用于简单方法作为参数函数。这是一个简单的例子:
import java.util.function.Function;
public class Foo {
private Foo(String parameter) {
System.out.println("I'm a Foo " + parameter);
}
public static Foo method(final String parameter) {
return new Foo(parameter);
}
private static Function parametrisedMethod(Function<String, Foo> function) {
return function;
}
public static void main(String[] args) {
parametrisedMethod(Foo::method).apply("from a method");
}
}
基本上,您有一个带有默认构造函数的 Foo
对象。一个 method
,将作为来自 Function<String, Foo>
类型的 parametrisedMethod
的参数调用。
Function
Foo::Method 对应于像 x -> Foo.method(x); 这样的 lambda
parametrisedMethod(Foo::method) 可以看作 x -> parametrisedMethod(Foo.method(x))
.apply("from a method") 基本上是做 parametrisedMethod(Foo.method("from a method"))
然后将在输出中返回:
>> I'm a Foo from a method
该示例应该按原样运行,然后您可以使用不同的类和接口从上述答案中尝试更复杂的东西。
Java-8 以上
从 Java 8 开始,您可以使用 lambda 表达式提供功能接口(只有一个抽象方法的接口)的抽象方法的实现,并将其作为参数传递给方法。
@FunctionalInterface
interface ArithmeticFunction {
public int calcualate(int a, int b);
}
public class Main {
public static void main(String args[]) {
ArithmeticFunction addition = (a, b) -> a + b;
ArithmeticFunction subtraction = (a, b) -> a - b;
int a = 20, b = 5;
System.out.println(perform(addition, a, b));
// or
System.out.println(perform((x, y) -> x + y, a, b));
System.out.println(perform(subtraction, a, b));
// or
System.out.println(perform((x, y) -> x - y, a, b));
}
static int perform(ArithmeticFunction function, int a, int b) {
return function.calcualate(a, b);
}
}
输出:
25
25
15
15
从 Method References 了解更多信息。
Java 确实有一种机制来传递名称并调用它。它是反射机制的一部分。您的函数应该采用类方法的附加参数。
public void YouMethod(..... Method methodToCall, Object objWithAllMethodsToBeCalled)
{
...
Object retobj = methodToCall.invoke(objWithAllMethodsToBeCalled, arglist);
...
}
我在这里没有找到任何解决方案来展示如何将绑定到它的参数作为方法的参数传递。 Bellow 是一个示例,说明如何传递已绑定参数值的方法。
第 1 步:创建两个接口,一个具有返回类型,另一个没有。 Java 也有类似的接口,但是它们没有什么实际用途,因为它们不支持抛出异常。
public interface Do {
void run() throws Exception;
}
public interface Return {
R run() throws Exception;
}
我们如何使用这两个接口在事务中包装方法调用的示例。请注意,我们使用实际参数传递方法。
//example - when passed method does not return any value
public void tx(final Do func) throws Exception {
connectionScope.beginTransaction();
try {
func.run();
connectionScope.commit();
} catch (Exception e) {
connectionScope.rollback();
throw e;
} finally {
connectionScope.close();
}
}
//Invoke code above by
tx(() -> api.delete(6));
另一个例子展示了如何传递一个实际返回一些东西的方法
public R tx(final Return func) throws Exception {
R r=null;
connectionScope.beginTransaction();
try {
r=func.run();
connectionScope.commit();
} catch (Exception e) {
connectionScope.rollback();
throw e;
} finally {
connectionScope.close();
}
return r;
}
//Invoke code above by
Object x= tx(() -> api.get(id));
使用观察者模式(有时也称为监听者模式):
interface ComponentDelegate {
void doSomething(Component component);
}
public void setAllComponents(Component[] myComponentArray, ComponentDelegate delegate) {
// ...
delegate.doSomething(leaf);
}
setAllComponents(this.getComponents(), new ComponentDelegate() {
void doSomething(Component component) {
changeColor(component); // or do directly what you want
}
});
new ComponentDelegate()...
声明实现接口的匿名类型。
这是一个基本示例:
public class TestMethodPassing
{
private static void println()
{
System.out.println("Do println");
}
private static void print()
{
System.out.print("Do print");
}
private static void performTask(BasicFunctionalInterface functionalInterface)
{
functionalInterface.performTask();
}
@FunctionalInterface
interface BasicFunctionalInterface
{
void performTask();
}
public static void main(String[] arguments)
{
performTask(TestMethodPassing::println);
performTask(TestMethodPassing::print);
}
}
输出:
Do println
Do print
我不是Java专家,但我可以这样解决您的问题:
@FunctionalInterface
public interface AutoCompleteCallable<T> {
String call(T model) throws Exception;
}
我在我的特殊接口中定义参数
public <T> void initialize(List<T> entries, AutoCompleteCallable getSearchText) {.......
//call here
String value = getSearchText.call(item);
...
}
最后,我在调用初始化方法时实现了 getSearchText 方法。
initialize(getMessageContactModelList(), new AutoCompleteCallable() {
@Override
public String call(Object model) throws Exception {
return "custom string" + ((xxxModel)model.getTitle());
}
})
带有反射的解决方案示例,传递的方法必须是公共的
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
public class Program {
int i;
public static void main(String[] args) {
Program obj = new Program(); //some object
try {
Method method = obj.getClass().getMethod("target");
repeatMethod( 5, obj, method );
}
catch ( NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
System.out.println( e );
}
}
static void repeatMethod (int times, Object object, Method method)
throws IllegalAccessException, InvocationTargetException {
for (int i=0; i<times; i++)
method.invoke(object);
}
public void target() { //public is necessary
System.out.println("target(): "+ ++i);
}
}
我很欣赏上面的答案,但我能够使用下面的方法实现相同的行为;从 Javascript 回调中借来的想法。尽管到目前为止一切都很好(在生产中),但我愿意更正。
这个想法是在签名中使用函数的返回类型,这意味着产量必须是静态的。
下面是一个运行超时进程的函数。
public static void timeoutFunction(String fnReturnVal) {
Object p = null; // whatever object you need here
String threadSleeptime = null;
Config config;
try {
config = ConfigReader.getConfigProperties();
threadSleeptime = config.getThreadSleepTime();
} catch (Exception e) {
log.error(e);
log.error("");
log.error("Defaulting thread sleep time to 105000 miliseconds.");
log.error("");
threadSleeptime = "100000";
}
ExecutorService executor = Executors.newCachedThreadPool();
Callable<Object> task = new Callable<Object>() {
public Object call() {
// Do job here using --- fnReturnVal --- and return appropriate value
return null;
}
};
Future<Object> future = executor.submit(task);
try {
p = future.get(Integer.parseInt(threadSleeptime), TimeUnit.MILLISECONDS);
} catch (Exception e) {
log.error(e + ". The function timed out after [" + threadSleeptime
+ "] miliseconds before a response was received.");
} finally {
// if task has started then don't stop it
future.cancel(false);
}
}
private static String returnString() {
return "hello";
}
public static void main(String[] args) {
timeoutFunction(returnString());
}