Java8.0 新特性一之函数式接口

函数式接口
  • 概念
    函数式接口在Java中是指:有且仅有一个抽象方法的接口。函数式接口,既适用于函数式编程场景的接口。而Java中的函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导。
    备注:“语法糖” 是指使用更加方便,但是原理湖边的代码语法。例如在遍历结合时使用发for-each语法,其实底层的实现原理仍然是迭代器,这便是"语法糖"。从应用层面来讲,Java中的Lambda可以被当做是匿名内部类的"语法糖",但是二者在原理上是不同的。

  • 格式
    只要确保接口中有且仅有一个抽象方法即可:

      修饰符interface接口名称{
      		public adstract 返回值类型 方法名称(可选参数信息);
      }
    
  • @FunctionalInterface注解
    与 @Override 注解的作用类似,Java 8中专门为函数式接口引入了一个新的注解: @FunctionalInterface 。该注解可用于一个接口的定义上:

      /**
      只有一个抽象方法的接口就是函数式接口
      */
      @FunctionalInterface //声明接口必须是一个函数式接口
      public interface MyInterface01 {
      		void show();
      }
    

代码使用:

public class Demo01 {

public static void main(String[] args) {
    //函数式接口,主要是在函数式编程(lambda)中使用
    //非函数式编程
    method01(new MyInterface01() {
        @Override
        public void show() {
            System.out.println("hello functional interface 111");
        }
    });
    //函数式编程:就是在匿名内部类对象的基础上,省略接口名及方法名.是语法糖
    method01(() -> {
        System.out.println("hello functional interface 111");
    });
    //简写方式:如果方法体中只有一行代码,可以省略方法体的大括号
    method01(()-> System.out.println("hello functional interface 111"));
	}

public static void method01(MyInterface01 inter01){
    
    inter01.show();
 }

}
  • 函数式接口的优势
    函数式接口比匿名内部类对象产生更少的字节码对象,提升java执行效率

  • 函数式接口作为参数,方法带参数
    接口:

      @FunctionalInterface
      public interface MyInterface02 {
      		void show(String msg , int num);
      }
    

代码使用:

public class Demo02 {

public static void main(String[] args) {

    //非函数式
    method(new MyInterface02() {
        @Override
        public void show(String msg, int num) {
            System.out.println(msg + " , " + num);
        }
    });
    //函数式
    method((String msg, int num) -> {
                System.out.println(msg + " , " + num);
            }
    );
    //简写方式一:省略了形参的类型
    method((msg, num) -> {
                System.out.println(msg + " , " + num);
            }
    );
    //简写方式二: 省略方法体的大括号
    method((msg, num) -> System.out.println(msg + " , " + num));

}

public static void method(MyInterface02 inter) {
    inter.show("hello", "你好");
}

}

  • 函数式接口作为返回值,方法带参数

      //定义函数式接口 
      public interface MyInterface02 { 
      		public abstract void show(String msg1, String msg2); 
      }
      public static void main(String[] args) { 
      		getInter1().show("你好", "函数式"); 
      		getInter2().show("你好", "函数式"); 
      }
      // 函数式完整 
      public static MyInterface02 getInter1() { 
      		return (String msg1, String msg2) ‐> { 
      			System.out.println(msg1 + msg2); 
      		}; 
      }
      // 函数式简写 
      public static MyInterface02 getInter2() { 
      		return (msg1, msg2) ‐> System.out.println(msg1 + msg2); 
      }
    
  • 函数式接口的方法有返回值

      //定义函数式接口 
      public interface MyInterface03 { 
      		public abstract String getMsg(); 
      }
    
      public static void main(String[] args) { 
      	showMsg(new MyInterface03() { 
      		@Override 
      		public String getMsg() { 
      			return "hello functional interface"; } 
      		}); 
      // lambada表达式
      showMsg(() ‐> { 
      		return "hello1 functional interface"; 
      	}); 
      	// lambda表达式简写 
      	showMsg(() ‐> "hello1 functional interface"); 
      	}
      	
      public static void showMsg(MyInterface03 inter) { 
      		String msg = inter.getMsg(); 		
      		System.out.println(msg); 
      }
    
函数式编程应用场景
  • 概念
    在兼顾面向对象特性的基础上,Java语言通过Lambda表达式使用函数式接口,就叫做函数式编程

  • 使用Lambda作为参数
    如果抛开实现原理不说,Java中的Lambda表达式可以被当做是i匿名内部类的替代品。如果方法的参数是一个函数式接口类型,那么就可以使用Lambda表达式进行替代

      public class Demo07 {
    
      public static void main(String[] args) {
    
      startThread(new Runnable() {
          @Override
          public void run() {
              System.out.println("线程要做的事");
          }
      });
    
      startThread(() -> {
                  System.out.println("线程要做的事");
              }
      );
    
      //比第一种的可阅读性强很多!!!
      startThread(() -> System.out.println("线程要做的事~~~"));
    
    }
    
      public static void startThread(Runnable runnable) {
      		new Thread(runnable).start();
        }
    
      }
    
  • 使用函数式接口作为返回值
    如果一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个Lambda表达式

      public class Demo08 {
    
      	public static void main(String[] args) {
    
      			Integer[] nums = {1 ,4 ,8,3};
      			Arrays.sort(nums,getComparator3());
      			for (Integer num : nums) {
          			System.out.println(num);
      			}
       }
    
      	public static Comparator<Integer> getComparator1(){
      		return new Comparator<Integer>() {
          		@Override
          		public int compare(Integer num1, Integer num2) {
              		return num1 - num2;
          		}
     	 };
      }
    
      	public static Comparator<Integer> getComparator2(){
      		return (num1 , num2) -> {
          		return num1 - num2;
      		};
      	}
    
       public static Comparator<Integer> getComparator3(){
      	//如果方法体中,只有一行代码,且该代码是一个返回语句,就可以省略大括号及return关键字
      	return (num1 , num2) -> num1 - num2;
      	}
    
      }
    
日志案例
  • 传统日志

      public class Demo09 {
    
       	public static void main(String[] args) {
      		String msg1 = "日志1";
      		String msg2 = "日志2";
      		String msg3 = "日志3";
      		printLog(2, msg1 + msg2 + msg3);	
      	}
      
      	public static void printLog(int level , String msg){
      		if (level == 1) {
          		System.out.println(msg);
      		}
      	}
    
      }
    

这段代码存在问题:无论级别是否满足 要求,作为log方法的第二个参数,三个字符串一定会首先被拼接并传入方法内,然后才会进行级别判断。如果级别不符合要求,那么字符串的拼接操作就白做了,存在性能浪费

  • 函数式日志

      	/**
      	* 提供日志内容
      	*/
      @FunctionalInterface
      public interface MyInterface06 {
      		String getMsg();
      }
    

代码实现:

public class Demo09 {

	public static void main(String[] args) {

    	//level=1 , 拼接msg1,msg2,msg3. 应该打印日志
    	//level=2 , 拼接msg1,msg2,msg3. 不应该打印日志

    	String msg1 = "日志1";
    	String msg2 = "日志2";
    	String msg3 = "日志3";
  

    	printLog(1, new MyInterface06() {
        	@Override
        	public String getMsg() {
            	return msg1 + msg2 + msg3;
        	}
    	});

    	printLog(1, () -> {
            	return msg1 + msg2 + msg3;
        	}
    	);
    	printLog(1 ,() -> msg1 + msg2 + msg3);

	}

	public static void printLog(int level , MyInterface06 inter){
    	if (level == 1) {
        	//只有当日志级别为1的时候,采取获取日志,并打印日志
        	System.out.println(inter.getMsg());
    	}
	}
}
常用函数式接口(Supplier接口)

JDK提供了大量常用的函数式接口以丰富Lambda的典型使用场景,它们主要在java.util.function包中被提供。下面是简单的几个接口及使用示例

  • Supplier接口
    java.util.function.Supplier接口仅包含一个无参的方法:T get()。 用来获取一个泛型参数指定类型的对象数据。由于这是一个函数式接口,这也就意味着对应的Lambda表达式需要“对外提供”一个符合泛型类型的对象数据

  • 基本使用

      public static void main(String[] args) {
    
      	//非函数式
      	String msg1 = getMsg(new Supplier<String>() {
          	@Override
          	public String get() {
              	return "abc";
          	}
      	});
      	System.out.println(msg1);
    
      	String msg2 = getMsg(() -> {
                 return "abc";
             });
      	System.out.println(msg2);
    
      	String msg3 = getMsg(() -> "abc");
      	System.out.println(msg3);
      }
    
       /**
      	* getMsg方法的返回值是由Supplier接口中的get方法返回!!!
      	*
      	* @param supplier
      	* @return
      	*/
      	public static String getMsg(Supplier<String> supplier) {
      		return supplier.get();
      	}
    
  • 综合案例

    • 需求:
      使用Supplier接口作为方法参数类型,通过Lambda表达式求出int数组中最大值。
      提示:接口的泛型请使用java.lang.integer类。

        public class Demo11 {
      
        	public static void main(String[] args) {
        		// 使用 Supplier 接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值。
        		Integer max1 = getMax(new Supplier<Integer>() {
        			@Override
        			public Integer get() {
            			int[] nums = {1, 2, 3, 4, 5};
            			int max = nums[0];
            			for (int num : nums) {
                			if (max < num) {
                    			max = num;
                			}
                      }
            			return max;
        			}
        		});
        		System.out.println(max1);
      
        		System.out.println("-----------");
      
        		Integer max2 = getMax(() -> {
        			int[] nums = {1, 2, 3, 4, 5};
        			int max = nums[0];
        			for (int num : nums) {
            			if (max < num) {
                			max = num;
            			}
        			}
        			return max;
        		});
      
        		System.out.println(max2);
        }
      
        /**
        * 获取最大值
        * @param supplier
        * @return
        */
        	public static Integer getMax(Supplier<Integer> supplier){
        		return supplier.get();
        	}
      
        }
      
常用函数式接口(Consumer接口)
  • Consumer接口java.util.function.Consumer 接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据,其他数据类型由泛型决定

  • accept方法Consumer接口中包含抽象方法void accept(T t),意为消费一个指定泛型的数据。基本使用如:

      public class Demo12 {
    
      public static void main(String[] args) {
    
      print(new Consumer<String>() {
          @Override
          public void accept(String s) {
              System.out.println(s);
          }
      },"helloworld1");
    
    
      print(( s)-> {
              System.out.println(s);
    
      },"helloworld2");
    
      print((msg) -> System.out.println(msg) ,"helloworld3");
    
      }
    
      //定义一个变量,由Consumer来消费该变量,就打印
      public static void print(Consumer<String> consumer , String msg) {
      consumer.accept(msg);
      }
    
    
      }
    
  • andThen方法
    如果 一个方法的参数和返回值全都是Consumer类型,那么就可以实现效果:消费数据时候,首先做一个操作,然后再做一个操作,实现组合。而这个方法就是Consumer接口中的default方法andThen

      default Consumer<T> andThen(Consumer<? super T> after) {
      		Objects.requireNonNull(after);
      		return (T t) -> { accept(t); after.accept(t); };
      }
    

注:java.uitl.Objects的requireNonNull静态方法将会在参数null时主动抛出NullPointerException异常。这省去了重复编写if语句和抛出空指针异常的麻烦

public class Demo13 {

//递归:同一个对象的方法调用自己
public static void main(String[] args) {
    consumeMsg("武汉加油", new Consumer<String>() {
        @Override
        public void accept(String s) {
            System.out.println(s);
        }
    }, new Consumer<String>() {
        @Override
        public void accept(String s) {
            System.out.println(s+"中国加油");
        }
    });

    consumeMsg("武汉加油", (String s) -> {
            System.out.println(s);
    }, (String s) -> {
            System.out.println(s+"中国加油");
    });

    consumeMsg("武汉加油!",msg-> System.out.println(msg+"a"),msg-> System.out.println(msg+"中国加油!"));

}

public static void consumeMsg(String msg , Consumer<String> consumer1 , Consumer<String> consumer2){
    consumer1.andThen(consumer2).accept(msg);
}

}
常用函数式接口(综合案例)

需求
下面的字符串数组当中存有多条信息,请按照格式“姓名:xx。性别:xx”的格式讲信息打印出来。要求将打印姓名的动作作为第一个Consumer接口的Lambda实例,将打印性别动作作为第二个Consumer接口的Lambda实例,将两个Consumer接口按照顺序“拼接”到一起

	String[] arrays = {"迪丽热巴,女", "老邱,男", "古力娜扎扎,女"};

实现

public class Demo14 {

public static void main(String[] args) {

    String[] arrays = {"迪丽热巴,女", "鑫磊,男", "古力娜扎扎,女"};
   
    printInfo(arrays,
            new Consumer<String>() {
                @Override
                public void accept(String arr) {
                    System.out.print("姓名:" + arr.split(",")[0] + ".");
                }
            },
            new Consumer<String>() {
                @Override
                public void accept(String arr) {
                    System.out.println("性别:" + arr.split(",")[1]);
                }
            }
    );

    printInfo(arrays,
            (arr) -> {
                System.out.print("姓名:" + arr.split(",")[0] + ".");

            },
            (arr) -> {
                System.out.println("性别:" + arr.split(",")[1]);

            }
    );

    printInfo(arrays,
            arr -> System.out.print("姓名:" + arr.split(",")[0] + "."),
            arr -> System.out.println("性别:" + arr.split(",")[1])
    );


}

public static void printInfo(String[] arrays, Consumer<String> consumer1, Consumer<String> consumer2) {
    for (String arr : arrays) {
        //consumer1和consumer2都在消费array数组中的每一个元素
        consumer1.andThen(consumer2).accept(arr);
    }
}

}
常用函数式接口(Predicate接口)
  • 有时候我们需要对某种类型数据进行判断,从而得到一个Boolean值结果,这时候可以使用java.util.function.Predicate接口

  • test方法
    Predicate接口中包含一个抽象方法:boolean(T t)。用户条件判断的场景

      public class Demo15 {
      // 判断字符是否等于4
      public static void main(String[] args) {
      		boolean isLength1 = isLength("hello", new Predicate<String>() {
          		@Override
          		public boolean test(String msg) {
              		return 4 == msg.length();
          		}
      		});
      		System.out.println(isLength1);
    
      		boolean isLength2 = isLength("hello", (String msg) -> {
          		return msg.length() == 4;
      		});
      		System.out.println(isLength2);
    
      		boolean isLength3 = isLength("hello", msg -> msg.length() == 4);
      		System.out.println(isLength3);
      }
    
      	public static boolean isLength(String msg,Predicate<String> predicate ){
      		return predicate.test(msg);
      	}
    
      }
    
  • 综合案例
    需求:
    将姓名长度为4,性别为女 的信息保存并打印

      public class Demo16 {
    
      public static void main(String[] args) {
      		String[] arrays = {"迪丽热巴,女", "鑫磊,男", "古力娜扎扎,女"};
      		// List<String> list = method(arrays, arr -> arr.split(",")[0].length() == 4 && arr.split(",")[1].equals("女"));
      		// System.out.println(list);
    
      		List<String> list1 = method1(
              		arrays,
              		arr -> arr.split(",")[0].length() == 4,
              		arr -> arr.split(",")[1].equals("女")
      		);
      		System.out.println(list1);
      }
    
      public static List<String> method(String[] arrays,Predicate<String> predicate){
      		List<String> list = new ArrayList<>();
      		for (String array : arrays) {
          		boolean test = predicate.test(array);
          	if (test) {
              	list.add(array);
          	}
      	}
      	return list;
      }
    
      public static List<String> method1(String[] arrays,Predicate<String> predicate1 ,Predicate<String> predicate2){
      		List<String> list = new ArrayList<>();
      		for (String array : arrays) {
          		boolean test = predicate1.negate().and(predicate2).test(array);
          	if (test) {
              	list.add(array);
          	}
     	 }
      	return list;
      }		
    
      }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值