首页 文章

Java Pass方法作为参数

提问于
浏览
217

我正在寻找一种通过引用传递方法的方法 . 我知道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());

14 回答

  • 197

    首先使用要作为参数传递的方法定义接口

    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 );
    }
    
  • 11

    使用 java.lang.reflect.Method 对象并调用 invoke

  • 17

    我不是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);
    ...
    }
    

    最后,我在调用 initialize 方法时实现 getSearchText 方法 .

    initialize(getMessageContactModelList(), new AutoCompleteCallable() {
              @Override
              public String call(Object model) throws Exception {
                return "custom string" + ((xxxModel)model.getTitle());
              }
            })
    
  • 6

    如果您不需要这些方法来返回某些内容,则可以使它们返回Runnable对象 .

    private Runnable methodName (final int arg){
        return new Runnable(){
           public void run(){
              // do stuff with arg
           }
        }
    }
    

    然后使用它像:

    private void otherMethodName (Runnable arg){
        arg.run();
    }
    
  • 24

    在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);
        }
    }
    
  • 11

    我没有找到任何关于如何使用 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 将作为 parametrisedMethod 的参数调用,其类型为 Function<String, Foo> .

    • Function<String, Foo> 表示该函数将 String 作为参数并返回 Foo .

    • 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
    

    该示例应该按原样运行,然后您可以使用不同的类和接口从上面的答案中尝试更复杂的东西 .

  • 1

    这是一个基本的例子:

    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
    
  • 52

    Java确实有一种传递名称并调用它的机制 . 它是反射机制的一部分 . 您的函数应该使用类Method的其他参数 .

    public void YouMethod(..... Method methodToCall, Object objWithAllMethodsToBeCalled)
    {
    ...
    Object retobj = methodToCall.invoke(objWithAllMethodsToBeCalled, arglist);
    ...
    }
    
  • 0

    从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);
    }
    
  • 4

    编辑:从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() 方法访问者 - 但是如果您需要遍历更复杂的对象图,那么值得检查 .

  • 0

    使用Observer模式(有时也称为Listener模式):

    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()... 声明实现接口的匿名类型 .

  • 0

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

    虽然这对于Java 7及更低版本尚未生效,但我相信我们应该展望未来,并至少认识到the changes会出现在新版本中,例如Java 8 .

    也就是说,这个新版本带来了lambdas和Java的方法引用(以及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,因为它有副作用 . 然而,它确实说明了以更直接可视化的方式使用它 .

  • 1

    我不认为lambdas是为了这个... Java不是一种函数式编程语言,因此我们不会将方法作为参数传递 . 话虽如此,请记住Java是面向对象的,考虑到这一点,我们可以做任何我们想做的事情 . 第一个想法是简单地传递一个“包含方法的对象”作为参数 . 因此,只要您需要“传递”该方法,只需传递该类的实例即可 . 请注意,在定义方法时,应将参数作为参数添加到包含该方法的类的实例 . 这应该可行,但它不是我们想要的,因为你不能重新定义方法,除非你有权访问类代码,并且在许多情况下这是不可能的;而且,我认为如果有人需要将方法作为参数传递,那是因为该方法的行为应该是动态的 . 我的意思是,使用您的类的程序员应该能够选择应该返回的方法,而不是它的类型 . 幸运的是,Java有一个美观而简单的解决方案:抽象类 . 简而言之,当您知道方法的“签名”时,会使用抽象类,但不知道它的行为......您可以将方法的名称和类型包装在抽象类中,并将该类的实例作为方法的参数......等等......是不是和以前一样?你可以有一个抽象类的实例吗?不和不......但是也是......当你创建一个抽象方法时你还必须在扩展抽象类的类中重新定义它,并且由于java的动态绑定,Java将始终(除非你声明它是静态的,私有的和其他一些东西)使用它的重新定义版本 . 这是一个例子......假设我们要将一个函数应用于数字数组:所以如果我们想要平方输入输出应该看起来像这样[1,2,3,4,...] - > [1,4,9,16 ,...](在像haskell这样的函数式编程语言中,这很简单,感谢像'map'这样的工具......) . 请注意,对于数字的平方没有什么特别之处,我们可以应用任何我们想要的函数吨 . 所以代码应该是这样的[args],f - > [f(args)] . 背部to java =>一个函数只是一个方法,所以我们想要的是一个将另一个函数应用于数组的函数 . 简而言之,我们需要将方法作为参数传递 . 这是我怎么做的==>

    1)定义WRAPPER抽象类和方法

    public  abstract class Function 
    {
        public abstract double f(double x);
    }
    

    2)使用APPLY_TO_ARRAY方法定义类

    public class ArrayMap 
    {
    public static double[] apply_to_array(double[] arr, Function fun)
    {
        for(int i=0; i<arr.length;i++)
        {
            arr[i]=fun.f(arr[i]);
        }
        return arr;
    }
    }
    

    3)创建一个TESTER-CLASS并且有一些乐趣

    public class Testclass extends Function
    {
        public static void main(String[] args) 
        {
            double[] myarr = {1,2,3,4};
            ArrayMap.apply_to_array(myarr, new Testclass());
            for (double k : myarr)
            {
            System.out.println(k);
            }
    
        }
    
        @Override
        public double f(double x) 
        {
    
            return Math.log(x);
        }   
    }
    

    请注意,我们需要传递一个Function类型的对象,因为Testclass扩展了我们可以使用它的Function类,所以强制转换是自动的 .

相关问题