我正在寻找一种通过引用传递方法的方法。我知道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表达式和方法引用更容易地传递方法。首先介绍一些背景知识:函数式接口是具有且仅有一个抽象方法的接口,尽管它可以包含任意数量的默认方法(Java 8新增)和静态方法。lambda表达式可以快速实现抽象方法,如果不使用lambda表达式,则无需使用所有不必要的语法。

没有lambda表达式:

obj.aMethod(new AFunctionalInterface() {
    @Override
    public boolean anotherMethod(int i)
    {
        return i == 982
    }
});

使用lambda表达式:

obj.aMethod(i -> i == 982);

以下是Java Lambda表达式教程的节选:

Syntax of Lambda Expressions A lambda expression consists of the following: A comma-separated list of formal parameters enclosed in parentheses. The CheckPerson.test method contains one parameter, p, which represents an instance of the Person class.Note: You can omit the data type of the parameters in a lambda expression. In addition, you can omit the parentheses if there is only one parameter. For example, the following lambda expression is also valid: p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25 The arrow token, -> A body, which consists of a single expression or a statement block. This example uses the following expression: p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25 If you specify a single expression, then the Java runtime evaluates the expression and then returns its value. Alternatively, you can use a return statement: p -> { return p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25; } A return statement is not an expression; in a lambda expression, you must enclose statements in braces ({}). However, you do not have to enclose a void method invocation in braces. For example, the following is a valid lambda expression: email -> System.out.println(email) Note that a lambda expression looks a lot like a method declaration; you can consider lambda expressions as anonymous methods—methods without a name.


下面是如何使用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);
    }
}

其他回答

在Java 8中,您现在可以使用Lambda表达式和方法引用更容易地传递方法。首先介绍一些背景知识:函数式接口是具有且仅有一个抽象方法的接口,尽管它可以包含任意数量的默认方法(Java 8新增)和静态方法。lambda表达式可以快速实现抽象方法,如果不使用lambda表达式,则无需使用所有不必要的语法。

没有lambda表达式:

obj.aMethod(new AFunctionalInterface() {
    @Override
    public boolean anotherMethod(int i)
    {
        return i == 982
    }
});

使用lambda表达式:

obj.aMethod(i -> i == 982);

以下是Java Lambda表达式教程的节选:

Syntax of Lambda Expressions A lambda expression consists of the following: A comma-separated list of formal parameters enclosed in parentheses. The CheckPerson.test method contains one parameter, p, which represents an instance of the Person class.Note: You can omit the data type of the parameters in a lambda expression. In addition, you can omit the parentheses if there is only one parameter. For example, the following lambda expression is also valid: p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25 The arrow token, -> A body, which consists of a single expression or a statement block. This example uses the following expression: p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25 If you specify a single expression, then the Java runtime evaluates the expression and then returns its value. Alternatively, you can use a return statement: p -> { return p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25; } A return statement is not an expression; in a lambda expression, you must enclose statements in braces ({}). However, you do not have to enclose a void method invocation in braces. For example, the following is a valid lambda expression: email -> System.out.println(email) Note that a lambda expression looks a lot like a method declaration; you can consider lambda expressions as anonymous methods—methods without a name.


下面是如何使用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);
    }
}

编辑:在Java 8中,lambda表达式是一个很好的解决方案,正如其他答案所指出的那样。下面的答案是为Java 7和更早的版本编写的…


看一下命令模式。

// 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所指出的,还有另一种使用访问者的方法。访问者方法稍微复杂一些——您的节点都需要使用acceptVisitor()方法来感知访问者——但如果您需要遍历一个更复杂的对象图,那么它就值得研究。

上次我检查的时候,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());

虽然这还不适用于Java 7及以下版本,但我相信我们应该展望未来,至少认识到Java 8等新版本中会出现的变化。

也就是说,这个新版本为Java带来了lambda和方法引用(以及新的api,这是这个问题的另一个有效解决方案)。虽然它们仍然需要一个接口,但不会创建新的对象,并且额外的类文件不会因为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);
}

输出将是真正的静态方法。方法引用可以是静态的、实例的、带有任意实例的非静态的,甚至是构造函数。对于构造函数,将使用类似ClassName::new的东西。

1有些人不认为这是lambda,因为它有副作用。不过,它确实以一种更直观的方式说明了如何使用。

下面是一个基本的例子:

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