功能接口/函数式接口 @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; } }
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()); } }
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