Author: Chen Changhao
1 Introduction
if...else... is often used in code, I heard that if...else... can be eliminated through the Function interface of Java 8! What is the Function interface? What if the if...else... is eliminated through the Function interface interface? Let's explore together.
2 Function interface
A Function interface is an interface that has one and only one abstract method, but can have multiple non-abstract methods. The Function interface can be implicitly converted into a lambda expression. The correctness of the Function interface can be verified through the FunctionalInterface annotation. Java 8 allows concrete methods to be added to interfaces. There are two specific methods in the interface, the default method and the static method.
@FunctionalInterface interface TestFunctionService { void addHttp(String url); }
Then you can use a Lambda expression to represent an implementation of the interface.
TestFunctionService testFunctionService = url -> System.out.println("http:" + url);
2.1 FunctionalInterface
2.1.1 Source code
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface FunctionalInterface {}
2.1.2 Description
The above picture is the annotation description of FunctionalInterface. Through the above annotations, you can know that FunctionalInterface is an annotation, which is used to indicate that an interface is a functional interface. A functional interface has only one abstract method. There can be default methods, because default methods have an implementation, so are not abstract. Instances of functional interfaces can be created with lambda expressions, method references, or constructor references.
FunctionalInterface will check whether the interface satisfies the functional interface:
- Type must be an interface type, not an annotation type, enum, or class.
- There can be only one abstract method.
- There can be multiple default and static methods.
- Abstract methods in java.lang.Object can be overridden explicitly.
The compiler will treat any interface that satisfies the definition of a functional interface as a functional interface, regardless of whether the FunctionalInterface annotation is used in the interface declaration.
3 Main categories of Function interface
Main categories of Function interface:
- Function: A Function function takes a parameter and returns a value.
- Supplier: The form of Supplier is that it does not accept parameters and only returns data.
- Consumer: Consumer receives a parameter and has no return value.
- Runnable: The form of Runnable is that it has no parameters and no return value.
3.1 Function
Function function takes a parameter and returns a value.
3.1.1 Source code
@FunctionalInterface public interface Function<T, R> { R apply(T t); default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } static <T> Function<T, T> identity() { return t -> t; } }
3.1.2 Method description
- apply: abstract method. Applies this function to the given arguments. The parameter t returns R by concrete implementation.
- compose: default method. Returns a composite function that first performs the fefore function applied to the input, and then applies the function to the result. If the evaluation of either function raises an exception, it is passed to the caller of the composed function.
- andThen: default method. Returns a composite function that first applies this function to its inputs, and then applies the after function to the result. If the evaluation of either function raises an exception, it is passed to the caller of the composed function.
- identity: static method. Returns a function that always returns its input arguments.
3.1.3 Method Example
1)apply
Test code:
public String upString(String str){ Function<String, String> function1 = s -> s.toUpperCase(); return function1.apply(str); } public static void main(String[] args) { System.out.println(upString("hello!")); }
The concrete implementation is invoked by apply. Results of the:
2)compose
Test code:
public static void main(String[] args) { Function<String, String> function1 = s -> s.toUpperCase(); Function<String, String> function2 = s -> "my name is "+s; String result = function1.compose(function2).apply("zhangSan"); System.out.println(result); }
Results of the
As shown in the result: compose first executes function2 and then executes function1.
3)andThen
Test code:
public static void main(String[] args) { Function<String, String> function1 = s -> s.toUpperCase(); Function<String, String> function2 = s -> "my name is "+s; String result = function1.andThen(function2).apply("zhangSan"); System.out.println(result); }
Results of the:
As the result shows:
andThen first executes function1 and then executes function2.
- identity
Test code:
public static void main(String[] args) { Stream<String> stream = Stream.of("order", "good", "lab", "warehouse"); Map<String, Integer> map = stream.collect(Collectors.toMap(Function.identity(), String::length)); System.out.println(map); }
Results of the:
3.2 Supplier
The performance of Supplier does not accept parameters and only returns data.
3.2.1 Source code
@FunctionalInterface public interface Supplier<T> { /** * Gets a result. * * @return a result */ T get(); }
3.2.2 Method description
get: abstract method. Return T by implementation.
3.2.3 Method example
public class SupplierTest { SupplierTest(){ System.out.println(Math.random()); System.out.println(this.toString()); } } public static void main(String[] args) { Supplier<SupplierTest> sup = SupplierTest::new; System.out.println("call once"); sup.get(); System.out.println("call twice"); sup.get(); }
Results of the:
As shown in the result: Supplier does not create a new class when it is established, and the value returned by each call to get is not the same.
3.3 Consumer
Consumer accepts one parameter and does not return a value.
3.3.1 Source code
@FunctionalInterface public interface Consumer<T> { void accept(T t); default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; } }
3.3.2 Method description
- accept: perform some operation on the given parameter T.
- andThen: Execute Consumer -> after in order, and if an exception is thrown by the execution operation, the exception is passed to the caller.
3.3.3 Method example
public static void main(String[] args) { Consumer<String> consumer = s -> System.out.println("consumer_"+s); Consumer<String> after = s -> System.out.println("after_"+s); consumer.accept("isReady"); System.out.println("========================"); consumer.andThen(after).accept("is coming"); }
Results of the:
As shown in the result: for the same parameter T, through the andThen method, the consumer is executed first, and then the fater is executed.
3.4 Runnable
Runnable: The form of Runnable is that it has no parameters and no return value.
3.4.1 Source code
@FunctionalInterface public interface Runnable { public abstract void run(); }
3.4.2 Method description
run: abstract method. The run method realizes the specific content, needs to put the Runnale into the Thread, start the thread through the start() method in the Thread class, and execute the content in the run.
3.4.3 Method example
public class TestRun implements Runnable { @Override public void run() { System.out.println("TestRun is running!"); } } public static void main(String[] args) { Thread thread = new Thread(new TestRun()); thread.start(); }
Results of the:
As shown in the result: when the thread executes the start method, the content in the run method of the Runnable is executed.
4 Function interface usage
The main purpose of Function is to realize the content of the method through lambda expression.
4.1 Difference handling
Original code:
@Data public class User { /** * Name */ private String name; /** * age */ private int age; /** * team member */ private List<User> parters; } public static void main(String[] args) { User user =new User(); if(user ==null ||user.getAge() <18 ){ throw new RuntimeException("Underage!"); } }
Results of the:
Code after using the Function interface:
@FunctionalInterface public interface testFunctionInfe { /** * Enter exception information * @param message */ void showExceptionMessage(String message); } public static testFunctionInfe doException(boolean flag){ return (message -> { if (flag){ throw new RuntimeException(message); } }); } public static void main(String[] args) { User user =new User(); doException(user ==null ||user.getAge() <18).showExceptionMessage("Underage!"); }
Results of the:
The specified exception information is thrown before and after using the function al interface.
4.2 Dealing with if...else...
Original code:
public static void main(String[] args) { User user =new User(); if(user==null){ System.out.println("New users"); }else { System.out.println("update user"); } }
Code after using the Function interface:
public static void main(String[] args) { User user =new User(); Consumer trueConsumer = o -> { System.out.println("New users"); }; Consumer falseConsumer= o -> { System.out.println("update user"); }; trueOrFalseMethdo(user).showExceptionMessage(trueConsumer,falseConsumer); } public static testFunctionInfe trueOrFalseMethdo(User user){ return ((trueConsumer, falseConsumer) -> { if(user==null){ trueConsumer.accept(user); }else { falseConsumer.accept(user); } }); } @FunctionalInterface public interface testFunctionInfe { /** * Different points handle different things * @param trueConsumer * @param falseConsumer */ void showExceptionMessage(Consumer trueConsumer,Consumer falseConsumer); }
Results of the:
4.3 Handling multiple if s
Original code:
public static void main(String[] args) { String flag=""; if("A".equals(flag)){ System.out.println("I'm A"); }else if ("B".equals(flag)) { System.out.println("I'm B"); }else if ("C".equals(flag)) { System.out.println("I'm C"); }else { System.out.println("no corresponding command"); } }
Code after using the Function interface:
public static void main(String[] args) { String flag="B"; Map<String, Runnable> map =initFunctionMap(); trueOrFalseMethdo(map.get(flag)==null).showExceptionMessage(()->{ System.out.println("no command"); },map.get(flag)); } public static Map<String, Runnable> initFunctionMap(){ Map<String,Runnable> result = Maps.newHashMap(); result.put("A",()->{System.out.println("I'm A");}); result.put("B",()->{System.out.println("I'm B");}); result.put("C",()->{System.out.println("I'm C");}); return result; } public static testFunctionInfe trueOrFalseMethdo(boolean flag){ return ((runnable, falseConsumer) -> { if(flag){ runnable.run(); }else { falseConsumer.run(); } }); }
Results of the:
5 summary
Function functional interface is a new feature added in java 8, which can be perfectly combined with lambda expressions. It is a very important feature and can greatly simplify the code.