New features in JDK8 – functional interfaces
Preface – Review lambda expressions
To understand what a functional interface is, we need to go back to lambda expressions. We know that to use a lambda expression, we need an interface that can only have one abstract method, such as the following example:
package com.features.functionInterface;
public class originalMethod {
public static void main(String[] args) {
Sum(a->{
int sum = 0;
for (int i : a) {
sum+=i;
}
return sum;
});
}
public static void Sum(Test test){
int[] a ={1.2.3.4.5.6.7.8.9};
int sum = test.getSum(a);
System.out.println("sum="+sum); }}// Functional interface
@FunctionalInterface
interface Test{
int getSum(int[] arr);
}
Copy the code
The problem is that we need functional interfaces and abstract methods every time we need a lambda expression. Lambda expressions, on the other hand, do not care about the interface name or abstract method name, but only the argument list and return value type of the abstract method.
Therefore, JDK8 provides a number of functional interfaces in the JDK to make lambda expressions easier to use.
Functional interface details
Types of functional interfaces
The location is lib\rt.jar! \ Java \util\function, here you can see the functional interface provided by the JDK
Here I choose a few more commonly used functional interface to explain, other if there is interest, you can read the source code.
Supplier
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get(a);
}
Copy the code
- No parameter has a return value used to produce data
- Example implementation: Find the maximum value in an array
package com.features.functionInterface;
import java.util.Arrays;
import java.util.function.Supplier;
// Use the JDK8 defined functional interface Supplier
// Supplier: no parameter has a return value to produce data
public class SupplierTest {
public static void main(String[] args) {
getMax(()->{
int[] a={1.2.5.3.6.4.8.9.7};
Arrays.sort(a);
return a[a.length-1];
});
}
public static void getMax(Supplier<Integer> supplier){
Integer max = supplier.get();
System.out.println("Max:"+max); }}Copy the code
Results:
Consumer
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
/**
* Returns a composed {@code Consumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*
* @param after the operation to perform after this operation
* @return a composed {@code Consumer} that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return(T t) -> { accept(t); after.accept(t); }; }}Copy the code
- A return value with no parameters is used to consume data
andThen
The default method is used to compose method implementations- Case implementation: string case conversion
package com.features.functionInterface;
import java.util.function.Consumer;
// Consumer: there is no return value for consuming data
public class ConsumerTest {
public static void main(String[] args) {
change(message -> System.out.println(message + "When converted to lowercase, becomes" + message.toLowerCase()));
changeAndThen(msg1 -> System.out.println(msg1 + "When converted to lowercase, becomes" + msg1.toLowerCase()),
msg2 -> System.out.println(msg2 + "Converted to uppercase, becomes" + msg2.toUpperCase()));
}
public static void change(Consumer<String> consumer) {
consumer.accept("Hello,Consumer");// Convert to lowercase
}
public static void changeAndThen(Consumer<String> c1, Consumer<String> c2) {
String str = "Hello,World";
// Call it separately
//c1.accept(str); // Convert to lowercase
//c2.accept(str); // Convert to uppercase
// In the second case, execute c1 and then c2
//c1.andThen(c2).accept(str);
// In the second case, execute c2 and then c1c2.andThen(c1).accept(str); }}Copy the code
Results:
Function
- Parameters return values: data of one type is derived from data of another type. The former is called a precondition, and the latter is called a postcondition.
andThen
Default method, composite implementationcompose
Default methods, composite implementations, andandThen
The effect on the contraryFunction
Static method that returns an argument passed by itself
@FunctionalInterface
public interface Function<T.R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
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));
}
/**
* Returns a function that always returns its input argument.
*
* @param <T> the type of the input and output objects to the function
* @return a function that always returns its input argument
*/
static <T> Function<T, T> identity(a) {
return t -> t;
}
Copy the code
- Case implementation: string and number conversion
package com.features.functionInterface;
import java.util.function.Function;
// Function: returns values with parameters from one type of data to another type of data. The former is called a precondition, and the latter is called a postcondition.
public class FunctionTest {
public static void main(String[] args) {
//test1(msg-> Integer.parseInt(msg));
// Can also be replaced by
test1(Integer::parseInt);
// andThen
test2(msg1-> {
int i = Integer.parseInt(msg1);
System.out.println("The string is converted to a number, resulting in:"+i);
return i;
}, msg2->{
String s = String.valueOf(msg2);
System.out.println("Number to string, result:"+s);
return s;
});
test3(msg1-> {
int i = Integer.parseInt(msg1);
System.out.println("The string is converted to a number, resulting in:"+i);
return i;
}, msg2->{
String s = String.valueOf(msg2);
System.out.println("Number to string, result:"+s);
return s;
});
}
public static void test1(Function<String,Integer> function){
String str = "123";
Integer apply = function.apply(str);// The string is converted to a number
System.out.println("The string is converted to a number, resulting in:"+apply);
}
public static void test2(Function<String,Integer> f1,Function<Integer,String> f2){
String str = "456";
//Integer i = f1.apply(str); // The string is converted to a number
//f2.apply(i); // Numbers are converted to strings
f1.andThen(f2).apply(str);
}
public static void test3(Function<String,Integer> f1,Function<Integer,String> f2){
Integer i = 789;
//String s = f1.apply(str); // Numbers are converted to strings
//f2.apply(s); // The string is converted to a numberf1.compose(f2).apply(i); }}Copy the code
Predicate
- The parameter has a return value, which is of Boolean type
and
Default method a&&b a and B are all corrector
The default method a | | b a, b have a rightnegate
Default method! A a right is wrong, a wrong is rightisEqual
A static method
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
/**
* Returns a predicate that represents the logical negation of this
* predicate.
*
* @return a predicate that represents the logical negation of this
* predicate
*/
default Predicate<T> negate(a) {
return(t) -> ! test(t); }default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
/**
* Returns a predicate that tests if two arguments are equal according
* to {@link Objects#equals(Object, Object)}.
*
* @param <T> the type of arguments to the predicate
* @param targetRef the object reference with which to compare for equality,
* which may be {@code null}
* @return a predicate that tests if two arguments are equal according
* to {@link Objects#equals(Object, Object)}
*/
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
Copy the code
- Case implementation: string operations
package com.features.functionInterface;
import java.util.function.Predicate;
// Predicate: parameters have return values. The return values are Boolean types
public class PredicateTest {
public static void main(String[] args) {
test(msg -> msg.length() > 3."hello");
test2(msg1 -> msg1.contains("h"), msg2 -> msg2.contains("r"));
}
public static void test(Predicate<String> predicate, String str) {
boolean test = predicate.test(str);
System.out.println("String greater than 3?" + test);
}
public static void test2(Predicate<String> p1, Predicate<String> p2) {
//boolean b1 = p1.test("h"); // if b1 contains h
//boolean b2 = p2.test("r"); // is there any r in b2
boolean flag1 = p1.and(p2).test("hello");// p1 && p2
boolean flag2 = p1.or(p2).test("hello"); // p1 || p2
boolean flag3 = p1.negate().test("hello"); / /! p1
System.out.println("Is there both h and r in hello?"+flag1);
System.out.println("Does hello contain either h or r:"+flag2);
System.out.println("Hello does not contain h:"+flag3); }}Copy the code