【问题标题】:How to define a instance-method-reference as an argument/parameter如何将实例方法引用定义为参数/参数
【发布时间】:2020-01-08 23:43:27
【问题描述】:

想象一个Person 对象通过调用bookAppointment 方法要求Scheduler 对象预约的情况。但现在无法预约。稍后,当一个约会打开时,一个约会将被预订——然后Person 对象想要得到通知。为了得到通知,我们希望将特定方法 appointmentHasBeenBooked 作为回调调用。

为了简单起见,假设我们没有要传递的参数,并且Person 上的 appointmentHasBeenBooked 方法没有返回值。

public class Person { 

    // Callback - To be invoked after the `Scheduler` object succeeds in booking an appointment. 
    public void appointmentHasBeenBooked() {
        …
    }

}

在Person 类的方法fooBar 的代码中,我们建立了一个Scheduler 对象。理想情况下,我将能够为该方法的回调传递一个简单的方法引用。这是我想象的foorBar 方法,因为它通过this::appointmentHasBeenBooked 方法参考。

public class Person { 

    public void fooBar() {
        Scheduler scheduler = … ;
        scheduler.bookAppointment( this.id , this:: appointmentHasBeenBooked ) ;  // Asking the scheduler to eventually book an appointment for this person, and when accomplished, notify this person by invoking the passed method.
        …
    }

    // Callback - To be invoked after the `Scheduler` object succeeds in booking an appointment. 
    public void appointmentHasBeenBooked() {
        …
    }

}

这可能吗?我还没有定义接口,更不用说FunctionalInterface。

➥ 如果可以,我如何将Scheduler 方法bookHasBeenAppointment 上的参数/参数定义为方法引用?

接收方法引用的方法如何调用该方法?

public class Scheduler {
    public void bookAppointment( UUID id , methRef ) {
        … book the appointment
        invoke_methRef ;
    }
}

在上面的代码中,我写的是什么:

  • methRef
  • invoke_methRef

我见过很多传递方法引用的例子,比如this::toString。但是我还没有看到任何关于定义正在传递的方法引用的接收端的解释。

我知道实现目标的更长迂回方式:使用单个方法 appointmentBooked() 定义接口 AppointmentBookingObserver。将该接口嵌套在 Scheduler 类上。将Person 声明为implements Scheduler. AppointmentBookingObserver。然后将bookAppointment 的第二个参数定义为AppointmentBookingObserver 对象。 bookAppointment 方法最终会调用单一接口方法:observer.appointmentHasBeenBooked。这行得通,但对于这样一个简单的单个回调场景来说似乎有点过分了。只需传递this::appointmentHasBeenBooked 就会如此更简单、更清晰。

这里不涉及线程。所以需要 Executors,Runnable,或者担心线程安全。

如果您希望了解我的实际用例场景的详细信息,而不是上面这个人为设计的 Person-Scheduler 示例,请参阅我的另一个问题,Vaadin LoginForm - signaling when user passed or failed authentication。

【问题讨论】:

    标签: java lambda arguments parameter-passing method-reference


    【解决方案1】:

    您认为的方法引用可以简单地是Runnable 类型

    class Scheduler {
    
      public void bookAppointment( UUID id, Runnable callback) {
        // book appointment
        callback.run();
      }
    }
    
    class Person {
    
      public void appointmentBooked() {
        // some code
      }
    }
    

    然后

    Scheduler scheduler = new Scheduler();
    Person person = new Person();
    
    scheduler.bookAppointment( person.id , person::appointmentBooked);
    

    【讨论】:

    • 我的问题不涉及线程,所以不需要Runnable。
    • @Basil 不涉及多线程。 Runnable 在这里只是一个功能接口。
    • 啊,我想我明白了,只需将Runnable 用作方便的预定义功能接口,而不是编写我自己的嵌套接口。很简单。我将不得不考虑这一点。谢谢!
    • @Basil 是的,这是满足您需求的最简单和最接近的匹配项。在此处查看其他选项docs.oracle.com/javase/8/docs/api/java/util/function/…
    • 我已经多次盯着that page。这些似乎都不适合调用没有返回值的无参数方法的简单情况。但是Runnable 就是这样。我唯一担心的是有人阅读我的代码并看到Runnable,会认为线程就在眼前。在该页面或其他地方是否有其他界面可以使用?我想我可以发明自己的来在我的代码库中使用。但这似乎是许多人的共同需求。
    【解决方案2】:

    在this question的cmets中已经承认,不为无参数void操作添加另一个功能接口的原因是Runnable已经实现了目的。

    所以你可以使用

    public class Scheduler {
        public void bookAppointment(UUID id, Runnable methRef) {
            … book the appointment
            methRef.run();
        }
    }
    

    被调用为

    public class Person { 
        public void fooBar() {
            Scheduler scheduler = … ;
            scheduler.bookAppointment(this.id, this::appointmentBooked) ;  // Asking the scheduler to eventually book an appointment for this person, and when accomplished, notify this person by invoking the passed method.
            …
        }
        // Callback - To be invoked after the `Scheduler` object succeeds in booking an appointment. 
        public void appointmentBooked() {
            …
        }
    }
    

    但还有其他选择

    public class Scheduler {
        public <T> void bookAppointment(UUID id, T instance, Consumer<T> methRef) {
            … book the appointment
            methRef.accept(instance);
        }
    }
    

    被调用为

    public class Person { 
        public void fooBar() {
            Scheduler scheduler = … ;
            scheduler.bookAppointment(this.id, this, Person::appointmentBooked) ;  // Asking the scheduler to eventually book an appointment for this person, and when accomplished, notify this person by invoking the passed method.
            …
        }
        // Callback - To be invoked after the `Scheduler` object succeeds in booking an appointment. 
        public void appointmentBooked() {
            …
        }
    }
    

    这强调了您问题的“instance-method-reference”方面,尽管此构造并不强制 Consumer 是这样的方法引用,但使使用这样的方法引用更容易。

    作为奖励,Person::appointmentBooked 是一个非捕获方法引用,它被评估为 Consumer 实例,该实例在后续调用中被重用。

    【讨论】:

      猜你喜欢
      • 2014-10-20
      • 1970-01-01
      • 2020-08-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-14
      • 1970-01-01
      相关资源
      最近更新 更多