functional interface and method reference

7 minute read

Published:

Java basics – functional interface and method reference

Functional interface and method reference

Functional interface

Concept

An interface with only one abstract method is called a functional interface

Format

Modifier interface interface name {
    public abstract return value type method name (optional parameter information);
    //Other non-abstract method content
}

Among them, public abstract can be omitted

@FunctionalInterface annotation

Can detect whether the interface is a functional interface

@FunctionalInterface
Modifier interface interface name {
    public abstract return value type method name (optional parameter information);
    //Other non-abstract method content
}

Use

Generally used as a method parameter or return value type

public static void show(MyFunctionalInterface myInter){
    myInter.method();
}
//transfer
show(new MyFunctionalInterfaceImpl());
show(new MyFunctionalInterface(){
    @Override
    public void method(){
        //...
    }
});
show(()->{
    //...
});

Functional Programming

Use Lambda as parameter and return value

  1. Use functional interfaces as method parameters

     public static void startThread(Runnable run){
         new Thread(run).start();
     }
     public static void main(String[] args){
         startThread(()->{
             //...
         });
     }
    
  2. Use a functional interface as the return value of a method

     public static Comparator<String> getComparator(){
         return (o1,o2)->o1.length()-o2.length();
     }
    

Commonly used functional interface

Many functional interfaces are provided in the java.util.function package

Supplier interface

The java.util.function.Supplier<T> interface only contains a parameterless method T get(), which returns generic type data (production interface)

public static String getString(Supplier<String> sup){
    return sup.get();
}
public static void main(String[] args){
    String str=getString(()->"abc");
}

Consumer interface

The abstract method of java.util.function.Consumer<T> interface void accept(T t) (consumer interface)

public static void setString(String name, Consumer<String> con){
    con.accept(name);
}
public static void main(String[] args){
   setString("abc",(name)->System.out.println(name));
}
  • Default method: andThen

    default Consumer<T> andThen(Consumer<? super T> after), combine two operations

public static void setString(String name, Consumer<String> con1, Consumer<String> con2){
    con1.andThen(con2).accept(name);
}

Predicate interface

java.util.function.Predicate<T> is used to judge data of a certain data type and get a boolean value

  • Abstract method: test

    boolean test(T t)

    public static boolean check(String s, Predicate<String> pre){
        return pre.test(s);
    }
    public static void main(String[] args){
        String s="aaa";
        boolean b=check(s,(s)->s.length()>5);
    }
    
  • Default method: and, or, negate

    default Predicate<T> and(Predicate<? super T> other)

    public static boolean check(String s, Predicate<String> pre1, Predicate<String> pre2){
        return pre1.and(pre2).test(s);
    }
    public static void main(String[] args){
        String s="aaa";
        boolean b=check(s,(s)->s.length()>5,(s)->s.contains("a"));
    }
    

    default Predicate<T> or(Predicate<? super T> other)

    public static boolean check(String s, Predicate<String> pre1, Predicate<String> pre2){
        return pre1.or(pre2).test(s);
    }
    public static void main(String[] args){
        String s="aaa";
        boolean b=check(s,(s)->s.length()>5,(s)->s.contains("a"));
    }
    

    default Predicate<T> negate()

    public static boolean check(String s, Predicate<String> pre){
        return pre.negate().test(s);
    }
    public static void main(String[] args){
        String s="aaa";
        boolean b=check(s,(s)->s.length()>5);
    }
    

Function interface

java.util.function.Function<T,R> is the conversion type interface

  • Abstract method: apply

    R apply(T t)

    public static void change(String s, Function<String,Integer> fun){
        System.out.println(fun.apply(s));
    }
    public static void main(String[] args){
        String s="1234";
        change(s,(s)->Integer.parseInt(s));
    }
    
  • Default method: andThen

    For combined operations

    public static void change(String s, Function<String,Integer> fun1, Function<Integer,String> fun2){
        System.out.println(fun1.andThen(fun2).apply(s));
    }
    public static void main(String[] args){
        String s="1234";
        change(s,(s)->Integer.parseInt(s),(s)->s+"!");
    }
    

Stream

Stream appeared after JDK1.8+, focusing on what to do instead of how to do it

list<String> list=new ArrayList();
list.add("abc");
list.add("aaaa");
list.add("bbbb");
list.add("aer");
list.stream().filter(name->name.startsWith("a")).filter(name->name.length()==3).foreach(name->System.out.println(name)) ;

Flow Thinking

Stream is a message queue from a data source

  • Elements are objects of a specific type, forming a queue (no elements are stored)
  • Data source: the source of the stream, which can be an array, a collection, etc.
  • Features:
    1. Pipelining: Intermediate operations will return the stream object itself, and multiple operations can be chained into a pipeline
    2. Internal iteration: You can directly call the traversal method
  • Steps for usage:

    1. Get a data source
    2. Data Conversion
    3. Perform operations and get results
  • Note:

    Stream is a pipeline stream, which can only be used once, and no other methods can be called after use

Get the stream

The java.util.stream.Stream interface is a newly added interface in JDK1.8+ (not a functional interface)

Ways to get the stream:

  1. All Collections can use the stream default method to get the stream default Stream<E> stream()
  2. The static method of of the Stream interface can get the stream corresponding to the array static <T> Stream<T> of(T... values)

Common methods

  • Delay method: the return type is still a method of the Stream interface’s own type
  • final method: a method whose return type is not the type of the Stream interface itself
  1. One by one: foeEach

    void forEach(Consumer<? super T> action)

    This method is a final method

  2. Filter: filter

    Stream<T> filter(Predicate<? super T> predicate)

    This method is a delayed method

  3. Mapping: map

    <R> Stream<R> map(Function<? super T,? Extends R> mapper)

    This method is a delayed method

  4. Statistics:count

    long count()

    This method is a final method

  5. Take the first few: limit

    Stream<T> limit(long maxSize)

    This method is a delayed method

  6. Skip the first few: skip

    Stream<T> long(long n)

    This method is a delayed method

  7. Combination: concat

    static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)

    Note: this is a static method

    Usage example:

    Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
    stream.filter(i->i>=2&&i<=4).map(i->i+"!").forEach(i-> System.out.println(i));
    Stream<String> stream1 = Stream.of("abc", "aaa", "def", "ddd", "abc", "aaa", "def", "ddd");
    long count = stream1.limit(5).count();
    System.out.println(count);
    Stream<Integer> stream2 = Stream.of(1, 2, 3, 4, 5, 6, 7);
    stream2.skip(3).forEach(i-> System.out.println(i));
    Stream<Integer> stream3 = Stream.of(1, 2, 3);
    Stream<Integer> stream4 = Stream.of(4, 5, 6);
    Stream.concat(stream3,stream4).forEach(i-> System.out.println(i));
    

    Method reference

    Redundant Lambda Expression

    @FunctionalInterface
    public interface Printable{
        publc void print(String s);
    }
    public class Main{
        public static void printString(Printable p){
            p.print("Hello World");
        }
        public static void main(String[] args){
            printString(s->System.out.println(s));
            printString(System.out::println);
        }
    }
    

    Format

    The double colon :: is a reference operator, and the expression in which it is used is a method reference.

    If the function scheme to be expressed by Lambda already exists in the implementation of a method, you can use method reference instead of Lambda expression

    Note: The parameters passed in Lambda must be the type that the method in the method reference can receive, otherwise an exception will be thrown

    Referencing member methods by object name

    Prerequisite: The object already exists, and the member method already exists

    class MethodObject{
        public void printUppercaseString(String s){
            System.out.println(s.toUpperCase());
        }
    }
    @FunctionalInterface
    interface Printable{
        public void print(String s);
    }
    public class Main {
        public static void printString(Printable p){
            p.print("Hello World");
        }
        public static void main(String []args)
        {
                
            MethodObject obj=new MethodObject();
            printString(obj::printUppercaseString);
        }
    }
    

    Referencing static methods by class name

    Prerequisite: the class exists, the static method exists

    class MethodObject{
        public static void printLowercaseString(String s){
            System.out.println(s.toLowerCase());
        }
    }
    @FunctionalInterface
    interface Printable{
        public void print(String s);
    }
    public class Main {
        public static void printString(Printable p){
            p.print("Hello World");
        }
        public static void main(String []args)
        {
            printString(MethodObject::printLowercaseString);
        }
    }
    

    Use super to reference parent class member methods

    Prerequisite: The parent class exists and the member method exists

    class MethodObject{
        public void printnum(String s) {
            int num=Integer.parseInt(s);
            System.out.println(num-1);
        }
    }
    class MethodObjectSon extends MethodObject{
        @Override
        public void printnum(String s) {
            int num=Integer.parseInt(s);
            System.out.println(num+1);
        }
        public void method(Printable p){
            p.print("123");
        }
        public void show(){
            method(super::printnum);
        }
    }
    @FunctionalInterface
    interface Printable{
        public void print(String s);
    }
    public class Main {
        public static void main(String []args)
        {
            new MethodObjectSon().show();
        }
    }
    

    Refer to this class member method through this

    Prerequisite: this class exists, member methods exist

    class MethodObject{
        public void printUpper(String s) {
            System.out.println(s.toUpperCase());
        }
        public void method(Printable p){
            p.print("abc");
        }
        public void show(){
            method(this::printUpper);
        }
    }
        
    @FunctionalInterface
    interface Printable{
        public void print(String s);
    }
    public class Main {
        public static void main(String []args)
        {
            new MethodObject().show();
        }
    }
    

    Class constructor reference

    Reference class construction method

    class MethodObject{
        public MethodObject(String s){
            System.out.println("MethodObject constructor! "+s);
        }
        public MethodObject(){}
        public void method(Printable p){
            p.print("abc");
        }
        public void show(){
            method(MethodObject::new);
        }
    }
    @FunctionalInterface
    interface Printable{
        public void print(String s);
    }
    public class Main {
        public static void main(String []args)
        {
            new MethodObject().show();
        }
    }
    

    Array constructor reference

    @FunctionalInterface
    interface ArrayBuilder{
        int[] buildArray(int length);
    }
    public class Main {
        public static int[] initArray(int length, ArrayBuilder builder){
            return builder.buildArray(length);
        }
        public static void main(String []args)
        {
            int[] array=initArray(10,int[]::new);
            System.out.println(array.length);
        }
    }