3

What is the Lambda syntax to pass and invoke a method reference?

Scenario: Building objects (radio buttons) based on values in an Enum.

I pass a Collection of the enum’s values. I want the other method (a constructor) to call a method on each of those enum values. That method call determines the displayed label for each radio button.

But the name of that method varies by each particular Enum class. Some enums have a getTitle method, another might have a method named getDescription or getCaption, and yet another might have getLocalizedVariationOfTitle. How do I let each calling programmer pass their particular method to be invoked?

Collection<SomeEnum> enumValues = Arrays.asList( SomeEnum.values() );
x = new EnumRadioButtons( enumValues , ??methodReferenceToGetTitle?? );

The syntax of passing, and invoking, ??methodReferenceToGetTitle?? eludes me. Constructor looks like this:

public EnumRadioButtons ( Collection<?> options , ??methodReferenceToGetTitle?? ) {
    …
    for ( Object option : options ) {
        this.setTitleOfEachOption( option , ??methodReferenceToGetTitle?? );
    }
}

When the method reference is dereferenced, the code effectively becomes:

this.setTitleOfEachOption( option , option.getTitle() );  // Pass the radio button item, and its label text.

or:

this.setTitleOfEachOption( option , option.getDescription() );

or:

this.setTitleOfEachOption( option , option.getSomeLocalizedVariationOfTitle() );

I tried passing MyEnum::getTitle for an enum named MyEnum that does indeed have a method getTitle(). But I get compiler errors.

I have a hunch this answer by might be use of a Supplier:

public EnumRadioButtons ( Collection<?> options , java.util.function.Supplier<String> supplierOfTitle ) {
    super( caption , options );
    for ( Object option : options ) {
        this.setTitleOfEachOption( option , supplierOfTitle.get() );
    }
}

As a workaround, I could require each Enum to implement an interface of TitleGetable such as:

public interface TitleGetable
{
    public String getTitle ();
}

But having all the calling programmers carry and explicitly implement this extra interface seems silly. Given this is a single method returning just a String, it seems like it is begging for a simple Lambda syntax.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154

1 Answers1

4

When you want users to use be able to use a lambda syntax all you have to do is take a parameter which is an interface with exactly one abstract function.
The callers decide to use a method reference, a lambda with the
() -> style or just implement the interface like a regular pre-java 8 interface. There are several predefined ones in java.util.function for convenience.

For better type safety it is probably better to use generics rather than the raw collections.

This would be one definition.

class EnumRadioButtons<T> {

    public EnumRadioButtons(Collection<T> options, Function<T,String> f ) {
        for (T option : options) {
            this.setTitleOfEachOption(option, f.apply(option));
        }
    }

    public void setTitleOfEachOption(T option, String title) {
          System.out.println("DEBUG - Setting title of option: " + option  + " to have title of: " + title );
    }
}

Which could be used for example with this enum:

enum MyEnum {
    A,B,C;

    public String myToString() {
        return toString();
    }
}

…and this main method class:

class User {
    public static void main(String[] args) {

        EnumRadioButtons<MyEnum> bts 
                = new EnumRadioButtons<>(Arrays.asList(MyEnum.values()),
                        MyEnum::myToString); // user of method reference
    }
}
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
WillShackleford
  • 6,918
  • 2
  • 17
  • 33
  • That does indeed work. Still cannot quite wrap my head around it, but it works. I created a new project using your three classes, worked. I added another method to experiment, I created an alternate method, `getTitleAsNow` with one line: `return ZonedDateTime.now( ).toString();` to more clearly show the effect. Thanks! – Basil Bourque Aug 26 '15 at 01:31
  • To see this Answer in action, see the `EnumBackedOptionGroup` class I wrote, posted at the top of [my Answer](http://stackoverflow.com/a/29402638/642706) on a separate Question. Thanks so much, I could not have built that class without this Answer. – Basil Bourque Aug 27 '15 at 23:34