Resource Uses for Functional Interfaces

Discussion in 'Spigot Plugin Development' started by AuroraLS3, Jul 29, 2018.

  1. Hi.

    This resource is intended for inspiration for experimentation with functional interfaces.

    Table of Contents

    • What are functional interfaces?
    • Code execution order with functional interfaces
    • Java 8 functional interfaces like Consumer, Supplier & Function
    • Example use cases
    What are functional interfaces?

    Functional interface is an interface with a single non-default method

    For Example:
    Code (Text):
    interface Foo {
        void bar();
    }
    Code (Text):
    interface ThingyMaBob {
        void doThat();

        default void doThis() {
             doThat();
        }
    }
    These interfaces can then be defined with lambdas or by method reference. Say you have a method goop(Foo foo) - You can define a new Foo in two ways (The variable declaration can omitted):
    Code (Text):
    void goop(Foo foo) {
        foo.bar();
    }

    // Lambda
    Foo foo = () -> doFooStuff();
    goop(foo);

    // Method reference
    Foo foo = this::doFooStuff; // static: Class::method
    goop(foo);

    When you add Generics, Functional interfaces become very interesting. - now you have a very versatile tool.
    Code (Text):
    interface Throwing<T extends Throwable>
        void call() throws T;
    }
    Code execution order with functional interfaces

    Sometimes you can delay a method call to a later point with functional interfaces. It is important to remember in what order the code is called to avoid mistakes.

    In this example the execution order can be unclear to some developers.
    Code (Text):
    goop(fooDoer.get()::doFooStuff);
    When the execution comes to this line, here is what happens:
    1. fooDoer.get() is called.
    2. The functional interface is created from the method reference
    3. goop is called with the functional interface
    This is important when you want your code to avoid calling blocking methods directly - What if fooDoer.get() blocks the thread for 10 seconds?

    This can be avoided with a lambda
    Code (Text):
    goop(() -> fooDoer.get().doFooStuff());

    Java 8 functional interfaces


    Java comes with plenty of very useful interfaces built in, so that you don't have to code your own in every project. Most commonly used are Supplier<T>, Consumer<T>, Predicate<T> and Function<T, R>

    All the interfaces can be found in https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html

    Supplier<T> Supplier can be used to return an object from a method with no parameters, all getters work as Supplier when used as method reference.

    Consumer<T> Consumer can be used to give a void return function a single parameter, all setters work as Consumer when used as method reference.

    Predicate<T> Predicate can be used to determine a boolean value from an object, Stream API filter method uses Predicate.

    Function<T, R> Function can be used to mutate an object into another, Stream API map method uses Function

    In addition to these, there are Bi- prefixed interfaces (Like BiConsumer<T, U>) that take two input arguments as well as UnaryOperator<T> which returns same type object it is given.

    Examples
    State object


    Last but not least I want to leave you with a couple use case examples I have found useful.

    First of the examples is the State object.
    Code (Text):
    class State {
        private int count;

        public State(int initialValue) {
            count = initialValue
        }

        public int getCount() {
            return count;
        }
        public void setCount(int c) {
            count = c;
        }

        public void operateOnState(StateOperation operation) {
             operation.operateOnState(this);
        }
    }
    Code (Text):
    interface StateOperation extends Consumer<State> {
        default void operateOnState(State state) {
            accept(state);
        }
    }
    With the operations separate from the object it is easier to keep the state mutating code and state itself separate. This is useful (as an example) when you're coding a GUI and command based operations, but want to keep State independent on the implementation of both the GUI and commands. This way ClickEvent will not get into the State object code.

    Example:
    Code (Text):
    class GUI {
       private State state;

       @EventHandler
       public void onClickEvent(InventoryClickEvent event) {
           int itemStackCount; // Get how many items are in the stack clicked
           state.operateOnState(state -> {
               state.setCount(state.getCount() + itemStackCount);
           }
       }
    }
    Code (Text):
    class Command {
       private State state;

       public boolean onCommand(...);

       public void addWithCommand(int givenArgument) {
           state.operateOnState(state -> {
               state.setCount(state.getCount() + givenArgument);
           }
       }
    }
    Abstract Messager

    I recently made a Pagination for a plugin, and I wanted it to be easily testable without mocking Bukkit classes.

    Functional interface Consumer<T> came to the rescue.

    When sending the paginated messages to the user, instead of giving Pagination CommandSender, I gave it the sendMessage method reference.
    This allowed testing with another method reference instead.

    Code (Text):
    class Pagination {
        public void sendPage(int page, Consumer<String> to) {
            // Pagination code
        }
    }
    Code (Text):
    class Command {
        private Pagination pagination;

        public void onCommand(..., CommandSender sender) {
            ....
            pagination.sendPage(page, sender::sendMessage);
        }
    }
    Code (Text):
    class PaginationTest {
        private Pagination pagination;

        @Test
        public void testPagination() {
            List<String> expected; // Expected values
            List<String> result = new ArrayList<>();
            ....
            pagination.sendPage(page, result::add);
            assertEquals(expected, result);
        }
    }
    Time optimized database fetch objects

    Storing Supplier instances in a Map. If a Supplier is not needed, its blocking method will not be called.
    This means that same method can be used for getting a single variable, or them all, with the same method.

    I'll have to save this one for a later date.

    https://github.com/Rsl1122/Plan-Pla...plan/data/store/containers/DataContainer.java ;)

    End

    Thanks for reading!

    Please comment below what cool use cases you've found for functional interfaces, if you learned something, or if I made any mistakes. :)
     
    • Useful Useful x 2
    • Informative Informative x 1
  2. Very nice explanation, I use Consumers in my lib for custom InventoryClickEvents hehe.
    Edit: I also use it CompletableFutures for databases, which take a Consumer :)
     
    #2 JustRayz, Jul 29, 2018
    Last edited: Jul 30, 2018
  3. Firestar311

    Supporter

    Very nice, most plugin developers do not know of this wonderful feature within Java. I have been using this myself for a while for my own server’s API.
     
  4. Benz56

    Moderator Supporter

    Awesome. This is exactly something that has endless use cases!

    One of the many things I've used this for is async operations. Call method -> do stuff async -> sync call with the result.

    Here is a very basic example for a database call:

    //Your method:
    Code (Java):
        public void hasUserData(UUID uuid, Consumer<Boolean> booleanConsumer) {
            Bukkit.getScheduler().runTaskAsynchronously(javaPlugin, () -> {
                try {
                    //Do your database call and retrieve the boolean or whatever else.

                   //"Return" the value synchronous.
                   Bukkit.getScheduler().runTask(javaPlugin, () -> booleanConsumer.accept(theBooleanValueRetrieved);
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            });
        }
    //Calling your method:
    Code (Java):
        getDatabase().hasUserData(getPlayer().getUniqueId(), bool -> {
            //The code in here is safely executed sync when the data has been retrieved asynchronous.
        });
    Do note this is very broad and quickly thrown together to give some inspiration.
     
  5. Yep. :)
    Runnable is also a functional interface - makes things very convenient.
    Code (Text):
    class Tasks {
        public taskOne() {
            ...
        }

        public taskTwo() {
            ...
        }

        public taskThree() {
            ...
        }

        public runTasks(JavaPlugin plugin, BukkitScheduler scheduler) {
            scheduler.runTask(plugin, this::taskOne);
            scheduler.runTask(plugin, this::taskTwo);
            scheduler.runTask(plugin, this::taskThree);
        }
    }
     
  6. I was kinda surprised you didn’t mention Runnable, a functional interface which has a return type of void and also no parameters. It’s useful in many cases if you “just” want “to do something.”