目录
函数式编程概念
函数式接口在java中是指:有且仅有一个抽象方法的接口
在java中的提现就是Lambda
语法糖备注:语法糖指原理不变但是更加方便的写法,例如增强for,从应用层面上讲,Lambda可以被当做匿名内部类的“语法糖”,但是二者上原理不同
格式(确保接口中只有一个抽象方法即可)
修饰符 interface 接口名称{
public abstract 返回值类型 方法名称(参数);
}
/*
函数式接口:只有一个抽象方法
但是可以有其他类型的方法(默认,静态,私有)
*/
@FunctionalInterface
//该注解用于指定该接口为函数式接口只能有一个抽象方法,有多个时会出现异常
public interface MyFunctionalInterface {
public abstract void method();
}
/*实现接口*/
public class Demo01 {
public static void main(String[] args) {
show(new MyFunctionalInterface() {
@Override
public void method() {
System.out.println("匿名内部类");
}
});
show(()-> System.out.println("Lambda")
);
}
public static void show(MyFunctionalInterface myInter){
myInter.method();
}
}
为什么说Lambda和匿名内部类原理上不同呢?
原因:
Demo01$1.class是创建的匿名内部类
而Lambda没有创建类,而且由于不用加载类,速度上也快一些
函数式编程
在兼顾面对对象的基础上使用Lambda表达式与方法引用等
Lambda的延迟执行:有些场景的代码执行后,结果不一定被使用,从而造成性能浪费,而Lambda表达式是延迟执行的,这正好可以作为解决方法,提升性能
/*浪费性能的日志案例*/
public class Demo2 {
//根据日志的级别,显示日志信息的方法
public static void showLog(int level,String message){
if (level==1){
System.out.println(message);
}
}
/*
发现以上代码存在性能浪费问题
调用showLog方法,传递的第二个参数是一个拼接的字符串
先把字符串拼接好再调用showLog方法
showLog方法如果传递的日志等级不是1级
那么字符串就白拼接了,造成了浪费
*/
public static void main(String[] args) {
String msg1="Hello";
String msg2="World";
String msg3="Java";
showLog(2,msg1+msg2+msg3);
}
}
为Lambda创建的函数式接口
package 函数式接口;
@FunctionalInterface
public interface MessageBuilder {
String builderMessage();
}
/*使用Lambda表达式优化日志案例
Lambda使用前提必须有一个函数式接口
*/
public class Demo3 {
public static void main(String[] args) {
String msg1="Hello";
String msg2="World";
String msg3="Java";
showLog(1,()-> msg1+msg2+msg3);
/*
使用Lambda表达式作为参数传递,仅仅是把参数传递到showLog方法中
只有if语句满足条件(日志等级=1)
才会调用MessageBuilder中的方法buildMessage进行字符串的拼接
如果条件不满住,那么buildMessage也不会执行,不会存在性能浪费
简单记:Lambda发生在函数调用后作为一个参数传递,再根据函数类的条件语句判断接口中的方法执不执行
*/
}
public static void showLog(int level,MessageBuilder mb){
if (level==1) System.out.println(mb.builderMessage());
}
}
Lambda可以作为参数同理Lambda也可以作为返回值
/*Lambda作为参数的多线程案例*/
public class Demo4 {
//Runnable是一个典型的函数式接口
public static void startThread(Runnable run){
new Thread(run).start();
}
public static void main(String[] args) {
startThread(()->{
System.out.println(Thread.currentThread().getName()+"-->"+"线程启动了");
});
}
/*
Lambda作为方法的返回值
Comparator接口案例
*/
@Test
public void test1(){
String[]arr={"aaa","bbbb","ccccc","dddddd"};
//排序前
System.out.println(Arrays.toString(arr));
Arrays.sort(arr,getComparator());
//排序后
System.out.println(Arrays.toString(arr));
}
public static Comparator<String> getComparator(){
return (String o1,String o2)-> o2.length()-o1.length();
}
}
Supplier接口
Supplier接口
java.util.function.Supplier<T>接口仅包含一个无参的方法:T get()
用来获取一个泛型参数指定类型的对象数据
/*常用的函数接口*/
public class Demo5 {
//Supplier<T>接口
// 被称为生产型接口,指定接口的泛型是什么数据,那么接口中的get方法就会产生什么类型的数据
public static String getString(Supplier<String> sup){
return sup.get();
}
@Test
public void test1(){
String s = getString(() -> {
//生产一个字符串并返回
return "这是一个字符串";
});
System.out.println(s);
}
//Supplier使用案例获取数组元素最大值
@Test
public void test2(){
int [] arr={100,32,120,33,22,15};
int maxElement = getMax(() -> {
int max = arr[0];
for (int i : arr)
if (i > max) max = i;
return max;
});
System.out.println("最大的元素是"+maxElement);
}
public static int getMax(Supplier<Integer> sub){
return sub.get();
}
}
Consumer接口
/*
java.util.function.Consumer<T>接口正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛型决定
抽象方法:accept(T t)意为消费一个指定泛型的数据
至于怎么消费,需要自定义
*/
public class Demo6 {
public static void method(String name,Consumer<String> consumer)
{
consumer.accept(name);
}
@Test
public void test1(){
method("无名者",(String name)->{
//反转输出
String reName=new StringBuilder(name).reverse().toString();
System.out.println(reName);
});
}
//Consumer中的默认方法andThen,如果一个方法的参数和返回值全是Consumer类型
//那么就可以在消费数据的时候先做一个操作,再做一个操作实现组合。这个方法就是andThen
/*源码
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
简单来说:
andThen需要两个Consumer接口,把两个Consumer接口组合再一起,再对数据进行消费
谁写前面谁先消费
*/
public static void andThenMethod(String s, Consumer<String> con1,Consumer<String> con2){
/* con1.accept(s);
con2.accept(s);*/
con2.andThen(con1).accept(s);//这里con2写前面故意先调用con2
}
@Test
public void test2(){
andThenMethod("Hello",
(t)->{
//将字符串大写输出
System.out.println(t.toUpperCase());
},
(t)->{
//将字符串小写输出
System.out.println(t.toLowerCase());
});
//使用andThen将两个接口连接到一起再消费
}
}
练习:
public static void printInfo(String[] arr,Consumer<String> con1,Consumer<String> con2)
{
for (String message : arr) {
con1.andThen(con2).accept(message);
}
}
@Test
public void test3(){
String[] arr={"无名氏1,女","无名氏2,女","无名氏3,男"};
printInfo(arr,(message)->{
//获取姓名
String name = message.split(",")[0];
System.out.print("姓名:"+name);
},(message)->{
String age=message.split(",")[1];
System.out.print("年龄:"+age+"。");
});
}
Predicate接口
/*java.util.function.Predicate<T>接口
包含一个抽象方法:boolean test(T t)用于对指定数据类型条件判断场景
*/
public class Demo7 {
//判断字符串的方法
public static boolean checkString(String s, Predicate<String> pre){
return pre.test(s);
}
@Test
public void test1(){
String s="abcde";
//复习一下能省略的东西:只有一个参数参数类型String可以省略,方法体只有一行{}可以省略
boolean b = checkString(s, (str) -> str.length()>5);
System.out.println(b);
}
/*
默认方法and
将两个Predicate条件使用“与”逻辑连接起来实现并且的效果
源码:
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
*/
//定义一个方法,传递一个字符串,两个Predicate接口
//一个判断字符串长度是否大于5,一个用于判断字符串中是否包含a
//两个条件必须同时满足
public static boolean checkString2(String s,Predicate<String> pre1,Predicate<String> pre2){
return pre1.negate().and(pre2).test(s);
}
@Test
public void test2(){
String s="asd";
boolean a = checkString2(s, (str) -> str.length() > 5, (str) -> str.contains("a"));
System.out.println(a);
}
/*
默认方法or方法
与and方法类似实现逻辑或关系
示例:
pre1.or(pre2).test(s);
negate:取非的方法
取反写法示例
pre1.negate().test(s)
pre1.negate().and(pre2).test(s);
*/
}
Function接口
import java.util.function.Function;
public class Demo8 {
/*
java.util.Function<T,R>接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。
抽象方法:R apply(T t),将t转化成R
*/
//应用案例将String转化成Integer
public static void translation(String s,Function<String,Integer> fun){
Integer in=fun.apply(s);
System.out.println(in);
}
@Test
public void test(){
String s="1234";
translation(s, Integer::parseInt);
//这里的双冒号代表访问Integer中的方法parseInt
}
/*
andThen方法,用来进行组合操作,也是先做。。后做。。
*/
public static void method(String s,Function<String,Integer> fun1,Function<Integer,String> fun2){
String str=fun1.andThen(fun2).apply(s);
System.out.println(str);
}
@Test
public void test2(){
String s="123";
//i+""空字符就是将Integer转化成字符串
method(s,(s1)-> Integer.parseInt(s1)+10,(i)-> i+"");
}
}