功能接口/函数式接口 @FunctionInterface

函数式接口就是只定义一个抽象方法并且添加@FunctionInterface的接口,如Runnable Callable Comparator。

函数式接口是为了Java中lambda使用而出现的

函数式接口特点:

  接口有且只有一个抽象方法

  允许定义静态非抽象方法

  允许定义default非抽象方法

  允许java.lang.Object中public方法

  FunctionInterface不是必须的,如果一个接口符合"函数式接口"定义,那么加不加该注解都没有影响

函数式接口作为方法的参数

如果方法的参数是一个函数式接口,可以使用Lambda表达式作为参数传递

public class Demo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        startThread(()-> System.out.println("start job"));
    }
    private static void startThread(Runnable runnable) {
        Thread t=new Thread(runnable);
        t.start();
    }
}

函数式接口作为方法的返回值 

public class Demo {
    public static void main(String[] args) {
                ArrayList<String> array=new ArrayList<String>();
        array.add("aa");
        array.add("b");
        array.add("cccc");
        array.add("ddd");

        System.out.println("排序前:"+array);
        Collections.sort(array);//自然排序
        System.out.println("自然排序后:"+array);
        Collections.sort(array,getComparator());
        System.out.println("指定比较器排序后:"+array); 
    }

    private static Comparator<String> getComparator(){
//        Comparator<String> comparator=new Comparator<String>() {
//            @Override
//            public int compare(String o1, String o2) {
//                return o1.length()-o2.length();
//            }
//        };
//        return comparator;

        return (s1,s2)->s1.length()-s2.length();
    }

 

注释部分是不使用lamda表示式代码

java内置四大函数式接口 --这些接口都是为了应对Lamda来使用的

函数式接口参数类型返回类型用途
Consumer 消费型接口 T 对类型为T的对象应用操作,包含方法:void accept(T t),常用于遍历 
Supplier 共给型接口 T 返回类型为T的对象,包含方法:T get() ,用于产生数据
Function<T, R> 函数型接口 T R 对类型为T的对象应用操作,并返回结果,结果是R类型的对象,包含方法:R apply(T t), 用于逻辑处理 
Predicate 断定型接口 T boolean 确定类型为T的对象是否满足某约束,并返回boolean值。包含方法boolean test(T t), 用于判断

consumer<T>实例:有输入没有返回

    public static void main(String[] args) {
         Consumer consumer = new Consumer<Object>() {
             @Override
             public void accept(Object o) {
                 System.out.println("o = " + o);
             }
         };
 ​
         consumer.accept("我是消费式函数接口");
     }

可以看出,传入了参数,没有返回值,故称为消费型

Supplier<T> 无中生有,没有输入,但有返回值

     public static void main(String[] args) {
         Supplier<String> supplier = new Supplier<String>() {
             @Override
             public String get() {
                 return "我是一个私生子";
             }
         };
         String s = supplier.get();
         System.out.println("s = " + s);
     }
 ​
 //运行结果
 s = 我是一个私生子

Predicate<T>里边抽象方法有参数,返回boolean,对数据进行校验作用

 public static void main(String[] args) {
         Predicate<String> predicate = new Predicate<String>() {
             @Override
             public boolean test(String s) {
                 if("1".equals(s)){
                     return true;
                 }
                 return false;
             }
         };
         boolean test = predicate.test("2");
         System.out.println("test = " + test);
     }
 ​
 //运行结果
 test = false

Function<T,R>,抽象方法中有入参,也有返回值,用于对代码进行逻辑处理

 

     public static void main(String[] args) {
         Function<String, Object> function = new Function<String, Object>() {
             @Override
             public Object apply(String s) {
                 return "传给我的参数是:" + s;
             }
         };
         Object tom = function.apply("Tom");
         System.out.println(tom);
     }
 ​
 //运行结果
 传给我的参数是:Tom

 

一、Stream

含义:Stream是对集合对象进行各种便利、高效聚合操作或者大批量数据操作。stream借助lambda表达式极大提高编程效率和程序可读性

 

构成:数据源——>数据转换——>执行操作获取想要结果。每次转换原有stream对象不变,返回一个新的stream对象

  例:

//实现将含有a、b、c三个元素的list转换为大写字母放入新的list中
List<String> strs=Arrays.asList("a","b","c");
//java8之前做法
List<String> before8=new ArrayList<String>();
for(String str:strs){
    before8.add(str.toUpperCase());  
}
//java8之后做法
List<String> after8=strs.stream().map(String ::toUpperCase).collect(Collector.toList());

//map作用是将对象转化为其他类型对象

 

stream()使用详解:

1、map():将input stream的每一个元素映射成output stream的另一个元素。--只是将原来元素做聚合等计算,但没有if条件判断

List<Integer> nums=Arrays.asList(1,2,3);
List<Integer> newNums=nums.stream().map(n->n*n).collect(Collectors.toList());

  结果:[1,4,9]

2、filter():过滤inputstream,将满足条件的元素生成outputstream  ---存在if条件判断

List<Integer> nums=Arrays.asList(1,2,3,4,5,6);
List<Integer> newNums=nums.stream().filter(n->n%2==0).collect(Collectors.toList());

  结果:[2, 4, 6]

3、forEach():对每个inputstream元素执行->后表达式

  

public class TestJava8 {
    public static void main(String[] args) {
        List<Integer> nums=Arrays.asList(1,2,3,4,5,6);
        nums.stream().forEach(n->getn(n));
        nums.stream().forEach(n->getn(n*8));
    }

        private static String getn(int n) {
        System.out.println("-----------"+n);
        return n+"dd";    
    }
}
结果:
-----------1
-----------2
-----------3
-----------4
-----------5
-----------6
-----------8
-----------16
-----------24
-----------32
-----------40
-----------48

 

4、reduce():作用是把stream元素组合起来

  reduce格式是:reduce(参考值,操作函数),参考值会参与到后边操作函数中;如果没有参考值则返回的Optional[]格式

  常见:sum、min、max、concat 等方法

List<Integer> lists=Arrays.asList(-3,-7,-1,5,1);
lists.stream().reduce(0,Integer::sum));//结果是0-3-7-1+5+1=-5
lists.stream().reduce(Integer::sum));//结果是Optional[-5]
lists.stream().reduce(Integer::sum).get());//结果是-5
lists.stream().reduce(Integer::min).get());//结果是-7 同理还有max()方法

List<String> strs=Arrays.asList("Q","W","E");
strs.stream.reduce("",String::concat)); //结果是"QWE"
strs.stream.reduce("A",String:concat));//结果是"AQWE"

 

 5、limit/skip

  limit(n):返回前n个元素

  skip(n):去掉前n个元素

  iterate():迭代,用于从函数生成流,一般用limit()或skip()对这种流加以限制

Stream.iterate(0, n->n+3).limit(10).skip(3).forEach(System.out::println);
结果:
9
12
15
18
21
24
27

 

6、Match匹配,stream有三个match方法

    allMatch():Stream中全部元素都符合逻辑判断,则返回true

    anyMatch():Stream中只要有一个元素都符合逻辑判断,则返回true

    noneMatch():Stream中全部元素都不符合逻辑判断,则返回true

        List<Staff> stuffs=Arrays.asList(
                new Staff("mkyong", 30),
                new Staff("jack", 27),
                new Staff("lawrence", 33),
                new Staff("lawrence", 22)
                );
        
        System.out.println(stuffs.stream().allMatch(stuff->stuff.getAge()>28));
        System.out.println(stuffs.stream().anyMatch(stuff->stuff.getAge()>28));
        System.out.println(stuffs.stream().noneMatch(sutff->sutff.getAge()>28));

结果:
    false
    true
    false

 

7、sort() 排序; distinct()找出不重复内容 

lists.stream().filter(word -> word.length() > 0).
 map(String::toLowerCase).
 distinct().
 sorted().
 collect(Collectors.toList());

 

8、groupby/partitionby

        List<Staff> stuffs=Arrays.asList(
                new Staff("mkyong", 30),
                new Staff("jack", 22),
                new Staff("lawrence", 33),
                new Staff("lawrence", 22)
                );
        Map<Integer,List<Staff>> stfs=stuffs.stream().collect(Collectors.groupingBy(Staff::getAge)); //分组
        for(Map.Entry<Integer, List<Staff>> entry:stfs.entrySet()) {
            System.out.println("key:"+entry.getKey());
            System.out.println("value:"+JSON.toJSONString(entry.getValue()));
        }

结果:
key:33
value:[{"age":33,"name":"lawrence"}]
key:22
value:[{"age":22,"name":"jack"},{"age":22,"name":"lawrence"}]
key:30
value:[{"age":30,"name":"mkyong"}]

 

 

二、方法引用

含义:方法引用是用来直接访问类或者实例的已存在的方法或者构造方法,方法引用是一个lambda表达式,其方法引用的操作符是双冒号::

 方法引用类型:  

类型  示例
引用静态方法 ContainingClass::staticMethodName
引用某个对象的实例方法 containingObject::instanceMethodName
引用某个类型的任意对象的实例方法 ContainingType::methodName
引用构造方法 ClassName::new

   

 

 

 

 

 

2.1引用静态方法:(类名::静态方法名)

组成语法:ClassName::statcimethodName

  诸如:String::valueOf  等价s->String.valueOf(s)

     Math::pow  等价于(x,y)->math.pow(x,y) 

 

public class Student {
    private String name;
    private double score;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }

    public Student(String name, double score) {
        this.name = name;
        this.score = score;
    }

}
View Code

 

package com.citi.ark.mls.service.qftest.methodrefer;

public class StudentCompare {
    //静态方法
    public static int compareStudentByScoreClz(Student s1, Student s2) {
        return (int) s1.getScore() - (int) s2.getScore();
    }
    //实例方法
    public int compareStudentByScoreObj(Student s1,Student s2){
        return (int) s2.getScore() - (int) s1.getScore();
    }
}

 

public class StaticMethodRefer {
    public static void main(String[] args) {
        Student s1=new Student("fff",75);
        Student s2=new Student("eee",70);
        Student s3=new Student("ccc",79);
        Student s4=new Student("ddd",65);
        List<Student> studentList= Arrays.asList(s1,s2,s3,s4);
        studentList.sort(StudentCompare::compareStudentByScoreClz);
        studentList.forEach(student -> System.out.println("name:"+student.getName()+",score:"+student.getScore()) );
    }
}

 

如上例,StudentCompare::compareStudentByScoreClz对类的静态方法引用,输出结果

name:ddd,score:65.0
name:eee,score:70.0
name:fff,score:75.0
name:ccc,score:79.0

2.2引用某个对象的实例方法(对象名::实例方法名)

 如上例中,将main方法改为如下

public class StaticMethodRefer {
    public static void main(String[] args) {
        Student s1=new Student("fff",75);
        Student s2=new Student("eee",70);
        Student s3=new Student("ccc",79);
        Student s4=new Student("ddd",65);
        List<Student> studentList= Arrays.asList(s1,s2,s3,s4);
        StudentCompare studentCompare=new StudentCompare();
        studentList.sort(studentCompare::compareStudentByScoreObj);
        studentList.forEach(student -> System.out.println("name:"+student.getName()+",score:"+student.getScore()) );
    }
}

 

 实例方法引用是在进行方法引用之前需要创建实例,通过实例对象名去引用方法

 输出结果:

 name:ccc,score:79.0

name:fff,score:75.0
name:eee,score:70.0
name:ddd,score:65.0

 2.3 类的实例方法引用(类名::实例方法名)

在方法调用中,是不能用类名直接调用实例方法的,但在方法引用中可以用用类名::实例方法这种形式通过类名直接调用实例方法

public class Student {
    private String name;
    private double score;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }

    public Student(String name, double score) {
        this.name = name;
        this.score = score;
    }
    //实例方法
    public int compareStudentByName(Student student){
        return this.getName().compareToIgnoreCase(student.getName());
    }
}
View Code

 

public class StaticMethodRefer {
    public static void main(String[] args) {
        Student s1=new Student("fff",75);
        Student s2=new Student("eee",70);
        Student s3=new Student("ccc",79);
        Student s4=new Student("ddd",65);
        List<Student> studentList= Arrays.asList(s1,s2,s3,s4);
        studentList.sort(Student::compareStudentByName);
        studentList.forEach(student -> System.out.println("name:"+student.getName()+",score:"+student.getScore()) );
    }
}

 

 从表面看,是类Student调用了compareStudentgByName,但Student是类而compareStudentByName不是静态方法,只是实例方法,所以Student类不能调用compareStudentByname.我们跟进List接口的sort方法,sort方法的参数是一个Comparator的函数式接口,该接口的抽象方法有两个参数.而我们的compareStudentByName方法只有一个参数,这要求的参数个数和我们传入的参数个数根本对应不上呀,那么lambada的另一个参数是谁呢,其实另一个参数另一个参数就是compareStudentByName方法的调用者即this(Student的当前实例)。

针对以上类的实例方法引用的问题,一般lambada表达式的第一个参数作为实例方法的调用者,其余参数作为实例方法的参数传入

 


default void sort(Comparator<? super E> c) {
  Object[] a = this.toArray();
  Arrays.sort(a, (Comparator) c);
  ListIterator<E> i = this.listIterator();
  for (Object e : a) {
    i.next();
    i.set((E) e);
  }
}



@FunctionalInterface
public interface Comparator<T> {
    int compare(T o1, T o2);
    ...
}

 

 2.4构造方法引用

构造方法引用就是通过new调用构造方法,创建一个对象

 

参考: https://blog.csdn.net/yelang0/article/details/100054075

https://zhuanlan.zhihu.com/p/549904376

 

posted on 2019-07-11 07:52  colorfulworld  阅读(329)  评论(0编辑  收藏  举报