1 Functional Interface
Functional interfaces in Java refer to interfaces that have one and only one abstract method
Functional interfaces, that is, interfaces for functional programming scenarios; while functional programming in Java is represented by Lambda, so functional interfaces are interfaces that can be used by Lambda; Lambda in Java can only be deduced smoothly if there is only one abstract method in the interface
Note: "Grammatical sugar" refers to a code grammar that is more convenient to use but far from being the same; for example, the for-earch grammar used when traversing a collection still implements an iterator at the bottom, which is "grammatical sugar". At the application level, Lambda in Java can be used as a "grammatical sugar" for anonymous internal classes, but the two are fundamentally different
Format: Just make sure there is only one abstract method in the interface
Modifier interface interface name { public abstract return value type method name (optional parameter information); //Other non-abstract method content }
Defining a functional interface is easy because public abstract, which is abstracted from an interface, can be omitted
public interface MyFunctionInterface{ void myMethod(); }
@FunctionalInterface annotation
Similar to the @Override annotation, Java8 introduces a new annotation specifically for functional interfaces: @FunctionalInterface, which can be used on the definition of an interface
Use of functional interfaces
/* Use of functional interfaces: parameters and return value types that are generally used as methods */ public class Demo{ // Define a method whose parameters use the functional interface MyFunctionalInterface public static void show(MyFunctionalInterface myInter){ myInter.method() } public static void main(String[] args){ // Call the show method, whose parameter is an interface, so you can pass the interface's implementation class object show(new MyFunctionInterfaceImpl); // The show method is called, and its parameter is an interface, so we can pass an anonymous internal class of the interface show(new MyFunctionInterface{ @Override public void method(){ System.out.println("Override abstract methods in interfaces using anonymous internal classes") } }); } // Calling a method whose parameters are a functional interface allows you to use Lambda expressions show(() -> { System.out.println("Use Lambda Abstract methods in expression override interfaces") }); // Lambda simplification show( -> System.out.println("Use Lambda Abstract methods in expression override interfaces")); }
2 Functional programming
Delayed execution of Lambda
Some scenarios do not necessarily result in a waste of performance when code is executed, while Lambda expressions are delayed, which can be just a solution to improve performance
Log case of performance waste
Logging can help us quickly locate problems and keep track of what's happening during program runs so that we can monitor and optimize projects
A typical scenario is where parameters are conditionally used, such as splicing log messages and printing out when conditions are met
public class Demo01Logger{ private static void log(int level,String msg){ // Method of displaying log information based on log level if(level == 1){ System.out.println(msg); } } public static void main(String[] args){ String msgA = "Hello"; String msgB = "World"; String msgC = "Java"; log(1,msgA+msgB+msgC); } }
The problem with the code above is whether the level s passed in are not 1 or seafood stitching characters, which results in a waste of performance
What we need to do is to shih the first parameter is not satisfied, do not perform the following operations; avoid waste of performance
// Interface Class @FunctionalInterface public interface MessageBuilder{ // Defines an abstract method of splicing messages to return the spliced information public abstract String builderMessaeg(); } public class Demo02Lambda{ // Defines a method for real-world logging whose parameters pass the level of the log and the MesageBuilder interface public static vid showLog(int level,MessageBuilder mb){ // Hierarchical judgement of the log, if level 1, calls the method in the interface if(level==1){ System.out.println(mb.builderMessage()); } } public static void main(String[] args){ String msgA = "Hello"; String msgB = "World"; String msgC = "Java"; // Call showLog method, parameter MessageBuilder is a functional interface, so Lambda expression can be used shoLog(1, () ->{ return msgA+msgB+msgC; }); } // A lambda expression is passed as a parameter, simply passing the parameter to the showLog method. When level=1, the method in the interface is called. If the condition is not satisfied, the method in the interface will not execute and there is no performance waste }
Use Lambda as parameter and return value
When using the comparator interface, Lambda expressions can be used to avoid overriding the compare method
3 Common Functional Interfaces
Supplier interface
Java.util.functionThe.Supplier<T>interface contains only one parameterless method: T get(); used to obtain object data of a generic specified type; since this is a functional interface, it also means that the corresponding Lambda needs to provide an external object data conforming to the generic type
Supplier<T>Interface is called a production interface. Specify what type of generic interface is, and what type will be returned by the get method in the interface
import java.function.Supplier public class Demo01Supplier{ private Static getString(Supplier<String> sup){ return sup.get(); } public static void main(String[] args){ String msgA = "Hello"; String msgB = "World"; System.out.println(getString(() -> msgA+msgB))); } }
Exercise: Maximizing elements in an array
Using the supplier interface as the method parameter type, the maximum value in the int array is obtained through the Lambda expression, which indicates that the generic interface should be usedJava.lang.Integerclass
public class Demo02Test{ public static int getMax(Supplier<Integer> sup){ return sup.get(); } public static vid main(String[] args){ int arr[] = {2,56,48,3,9,64}; // Call getMax method, parameter pass Lambda int manNum = getMax(() -> { // Calculate the maximum value in an array int max = arr[0]; // foreach for(int i:arr){ if(i>max){ max = i; } } return max; }); System.out.println("The largest element in an array"+max); } }
Consumer interface
Java.util.functionThe.Consumer<T>interface, on the contrary to Supplier, does not produce a data, but consumes a data whose data type is determined by its type
Abstract method: accept
The Consumer interface contains the abstract method void accept (T), which means to consume a specified generic type of data, basically using
import javautil.function.Consumer public class Demo01Consumer { private static void consumeString(String name,Consumer<String> con){ con.accept(name); } public static int method(Supplier<Integer> sup){ consumeString(s -> System.out.println(s)); } }
A better way is to use method references
Default method: andThen()
If the parameters and return values of a method are of Consumer type, then the effect can be achieved: when consuming data, the implementation first does an operation, then does an operation to achieve a combination; and this method is the default method in the interface, andThen, the source code is
default Consumer<T> andThen(Consumer<? super T> after){ Objects.requireNonNull(after); return (T t) -> {accept(t);after.accept(t);}; }
Remarks:Java.util.ObjectsThe requireNonNull static method of will actively throw a NullPointerException exception when the parameter is null, eliminating the hassle of repeating if statements and throwing null pointer exceptions
To achieve composition, two or more Lambda expressions are required, while andThen's semantics are formally "step by step", such as when two steps are combined:
public class Demo02AndThen{ public static void method(String s,Consumer<String> con1,Consumer<String> con2){ // Connect two consumer s and consume again con1.andThen(con2).accept(s); } public static void main(String[] args){ meth("hello",(t) ->{ System.out.println(t.toUpperCase()); }, (t) ->{ System.out.println(t.toLowerCase()); }) } }
Format Print Information
public class Demo03Test{ public static void printInfo(String[] arr,Consumer<String> con1,Consumer<String> con2){ // Traversing through an array of strings for(String message: arr){ con1.andThen(con2).accept(messge); } } public static void main(String[] args){ // Define an array of string types String[] arr = {"Dili Reba,female","Gulinaza,female","Malzahar,male"}; printInfo(arr,(message)->{ String name = meaasge.split(",")[0]; System.out.print("Full name: "+name); },(meaasge)->{ String age = messaeg.aplit(",")[1]; System.out.println("Age :"+age); }) } }
Predicate interface
Sometimes we need to make a judgment about a certain type of data to get a boolean result, which can then be usedJav.util.function.Predicate<T>Interface
Abstract method: test
The Predicate interface contains an abstract method: Boolean test\<T> for conditional scenarios
public class DemoPredicateTest{ public static void method(Predicate<String> preficate){ boolean veryLong = predicate.test("HelloWorld"); System.out.println("Is the string long? :"+veryLong); } public static void main(String[] args){ method(s -> s.length() >5); } }
The criterion for conditional judgment is the Lambda expression logic passed in, which is considered long if the string length is greater than 5
Default method: and
Since conditional judgment exists, there are three common logical relationships with, or, not with, and when two predicate conditions are joined with AND logic to achieve the effect of AND, the default method and JDK source code are
default Predicate<T> and(Predicate<? super T> other){ Objects.requireNonNull(other); return (t) ->test(t) && other.test(t); }
If you want to determine that a character contains both the uppercase "H" and the uppercase "W":
public class DemoPredicateAnd{ public static void method(Predicate<String> one,Predicate<String> two){ boolean isValid = one.and(two).test("HelloWorld"); System.out.println("Is the string long? :"+isVaild); public static void main(String[] args){ method(s -> s.contains("H"),s -> s.contains("W")); } } }
Default method: or
Similar to and, the default method or implements logical relationships or, JDK source code is
default Predicate<T> or(Predicate<? super T> other){ Objects.requireNonNull(other); return (t) ->test(t) || other.test(t); }
If the implementation logic string contains the uppercase H or W, then the big code Chinese medicine and Western medicine can be modified to or, other unchanged
public class DemoPredicateOr{ public static void method(Predicate<String> one,Predicate<String> two){ boolean isValid = one.or(two).test("HelloWorld"); System.out.println("Is the string long? :"+isVaild); public static void main(String[] args){ method(s -> s.contains("H"),s -> s.contains("W")); } }
Default method: negate
And, as you already know, the rest of the non (reverse) code is very simple, with the default negate JDK source code being
default Predicate<T> negate(){ return (t) -> !test(t); }
It is easy to see from the implementation that after it executes the test method, it does the boolean value of the result! Instead, it does; it must call the negate method before the test method is called, just like the and or methods.
public class DemoPredicateNegate{ public static boolean checkString(String s,Predicate<String> pre){ return !pre.test(s); } public static void main(String[] args){ String = "abc" boolean b = checkString(s,(String str)-> { return str.length > 5 }); } }
Exercise: Collection Information Filtering
There is more than one Name + Gender information in the array as follows. Please filter the required strings into the set ArrayList by splicing through the Pridicate interface, satisfying both of the following conditions:
- Must be a girl
- Name 4 words
public class DemoPredicate{ public static void main(String[] args){ String[] arra = {"Dili Reba,female","Gulinaza,female","Malzahar,male","Zhao Liying,female"} } }
public class DemoTest{ public class ArrayList<String> filter(String[] arr,Predicate<String> pre1,Predicate<String> pre1){ ArrayList<String> list = new ArrayList<>(); for(String a:arr){ // Use the test method in the interface to judge the acquired characters boolean b = pre1.and(pre2).test(s); if(b){ list.add(s) } } return list } public static void main(String[] args){ String[] array = {"Dili Reba,female","Gulinaza,female","Malzahar,male","Zhao Liying,female"} // Call filterffa filter(array,(String s) ->{ s.split(",")[1].equals("female"); },(String s) ->{ s.split(",")[0].length == 4; }); // Traverse Set for(String s:list){ System.out.println(s); } } }
Function Interface
Java.util.functionThe.Function<T, R>interface is used to derive data from one type of data that is called a precondition and a postcondition.
Abstract method: apply
The main abstract method in the Function interface is: R apply(T), which gets the result of type R based on the parameter of type T.
Scenarios used: for example, converting a String type to an Integer type
public class DemoFunctionApply{ private static void change(Function<String,Integer> function){ int num = function.apply(s); System.out.println(num); } public static void main(String[] args){ String s = "1234"; change(s,(String str) -> return Integer.parseInt(s)); } }
Default method: andThen
The andThen method is used for combining operations. The JDK source code is as follows
default <V> Function<T,V> andThen(Function<? super R,? extends V>after){ Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); }
This method is also used in what-to-do-first scenarios, similar to andThen in Constumer:
public class DemoFunctionAndThen{ public static void change(String s,Function<String,Integer> fun1,Function<String,Integer> fun2){ String ss = fun1.andThen(fun2).apply(s); System.out.println(ss); } public static void main(String[] args){ String = "123"; change(s,(String str) ->{ return Integer.parseInt(str)+10 },(Integer i) ->{ return i+""; }); } }
Exercise: Custom function type splicing
Use Function to stitch together the function models. Multiple function operations that need to be performed sequentially are
String str = "Zhao Liying, 20";
- Intercept the number age part of the string to get the string;
- Convert the string from the previous step to a number of type int;
- Add up the int number of the previous step to 100 to get the result int number.
public class Demo03Test{ public static int change(String s,Function<String,String> fun1,Function<String,String> fun2,Function<String,String> fun3){ // Using the andThen method to group the three together return fun1.andThen(fun2).andThen(fun3).apply(s); } public static void main(String[] args){ String str = "Zhao Liying,20"; int num =change(str,(String s)->{ return s.split(",")[1]; },(String s)->{ return Integer.parseInt(s); },(Integer i)->{ return i+100 }); System.out.println(num); } }