Java Functional Interface--Abstract Method Interface

Posted by chris_s_22 on Sat, 13 Jun 2020 03:57:12 +0200

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:

  1. Must be a girl
  2. 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";

  1. Intercept the number age part of the string to get the string;
  2. Convert the string from the previous step to a number of type int;
  3. 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);
    }
}

Topics: Lambda Java JDK Programming