03 函数式接口编程

03 函数式接口编程

1. 函数式编程思想

面向对象编程 是对数据进行抽象,而函数式编程是对【行为进行抽象】。
通过函数式编程,程序员能编写出更容易阅读的代码,
这种代码更多地表达了业务逻辑,而不是从机制上如何实现。

从 函数式编程思想的角度去思考问题 ,最重要的就是 函数的输入和输出;

函数式编程思想改变了代码结构,将注重点放在了参数和返回值上。
    函数式编程思想,通过使用一个 不可变值 和 函数,
    函数对这个值进行处理,映射成另一个【任意的】【函数式接口类型的】值

2. 函数式接口:

1)概念:
            函数式接口就是只定义**【一个抽象方法】**的接口。
            
            注 : 哪怕这个接口中有很多默认 / 静态方法,
                  只要在这个接口中只定义了一个抽象方法 , 它就是一个函数式接口。

2)函数式接口的作用:
	        Lambda表达式允许你直接以内联的形式为函数式接口的抽象方法提供实现,
	        并把整个表达式作为函数式接口的实例。
	
	        用匿名内部类也可以完成同样的事情,只不过比较笨拙。
                
                eg    :    Comparator c = Lambda表达式
	                       TreeSet t = new TreeSet(c);
3) 函数描述符
	        函数式接口中的抽象方法的名基本上就是Lambda表达式的名,
	        我们将这种抽象方法叫作函数描述符。

4)声明函数式接口:	`@FunctionalInterface`
	        这个标注用于表示该接口会设计成一个函数式接口
	        如果不满足函数式接口,会报错

5)java常用的函数式编程接口使用:
	        i.     比较器	
           ii.     实现线程
	      iii.     绑定事件 	
           iv.    集合数据排序

3. JDK自带的函数式接口上使用lambda表达式

    JDK 1.8 API中包含了很多内建的函数式接口,
    在老Java中常用到的比如Comparator或者Runnable接口,
    这些接口都增加了@FunctionalInterface注解以便能用在lambda上。

    Java 8 API同样还提供了很多全新的函数式接口来让工作更加方便,
    使用函数式编程接口,配合lambda表达式,可以以更少的代码为API方法添加更多的动态行为。
 **java.util.function包下的常用函数式接口** 
    1)Predicate  接口		
	    抽象方法:    	boolean = test(T t) 
	    参数列表:	        泛型T对象(泛型必须要传入一个包装器类型的数据)
	    返回值类型:	boolean。
   	
   	    功能:可以通过这个抽象方法结合Lambda表达式,自定义一个判断规则,
   			对传入的对象进行判断
/**
 * 	把集合中满足要求的数据,进行打印
 * 	要求不确定,每次代码的要求可能都不一样
 * @author Charlie
 *    可以提供一个遍历筛选的方法,然后将要进行筛选的集合 以及 判断器 传入这个方法
 *    这样只需要调用这个方法,然后每次传入的判断器不同,得到的筛选结果也不同
 */
public class Test4 {

	public static void show(List<String> list,Predicate<String> pre) {
		//遍历集合,把每个元素进程判断是否满足要求
		for (String msg : list) {
			//判断本次msg是否满足要求
			boolean test = pre.test(msg);
			if(test) {
				System.out.println(msg);
			}
		}
		
	}
	
	public static void main(String[] args) throws Exception {
		List<String> list = new ArrayList<String>();
		list.add("Wendy");
		list.add("Charlie");
		list.add("lisi");
		list.add("wangwu");
		list.add("zhaoliu");
		list.add("zhaosan");
		
		//构建接口实现类对象
		//由于本次接口为函数式接口,所以可以使用lambda表达式写
		Predicate<String> p = (msg)->{return true;}; //获取所有的
		
		Predicate<String> p1 = (msg)->{
			return msg.length() > 5;    //获取字符串长度大于5的
		};
		//Predicate p11 = (msg)-> msg.length() > 5; //简写
		show(list,p);
		System.out.println("------------------");
		show(list,p1);
		System.out.println("------------------");
		
		//返回"z"开头的集合元素
		Predicate<String> p2 = (msg)->{ 
			//return (msg.charAt(0) == 'z');
			return (msg.startsWith("z"));
		};
		show(list,p2);
		System.out.println("------------------");
		
		//返回"u"结尾的集合元素
		Predicate<String> p21 = (msg)->{ 
			return msg.endsWith("u");
		};
		show(list,p21);
		System.out.println("------------------");
	}
}		
    输出结果:
Wendy        
Charlie
lisi
wangwu
zhaoliu
zhaosan
------------------
Charlie
wangwu
zhaoliu
zhaosan
------------------
zhaoliu
zhaosan
------------------
wangwu
zhaoliu
------------------

** Predicate接口还有几个默认方法 and 、 or 、~ … **

//    and方法:
default Predicate<T> and(Predicate<? super T> other) {
	Objects.requireNonNull(other);	//判断后面的other是否为空
	    return (t) -> test(t) && other.test(t);
	}
	return (t) -> { return test(t) && other.test(t);};
}

//    or方法
default Predicate<T> or(Predicate<? super T> other) {
    Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
	}
	return (t) -> { test(t) || other.test(t); };   
}

//    negate方法
default Predicate<T> negate() {
    return (t) -> !test(t);
}
    测试and 、or方法:
//返回以"z"开头的集合元素,且以"u"结尾的集合元素
Predicate<String> p3 = p21.and(p2);
show(list,p3);
System.out.println("------------------");
		
//返回以"z"开头的集合元素,且以"u"结尾的集合元素,或者长度大于5的
Predicate<String> p4 = p1.or(p3);
show(list,p4);	
    测试结果:
zhaoliu
------------------
Charlie
wangwu
zhaoliu
zhaosan
    2)Function  接口		
	    抽象方法:        R apply(T t);	
	    参数列表:	        泛型 T
	    返回值类型:     泛型 R
	
	    功能:可以通过这个抽象方法结合Lambda表达式,自定义一个功能,
			对传入的对象执行一系列方法,产生结果 
			
eg:	 		
public class Test5 {				
	public static void show(List<String> list,Function<String,Integer> f) {
		for (String msg : list) {
			//将msg交给Function接口下的处理方法
			//处理完以后返回一个数据
			//这个数据被打印
			Integer apply = f.apply(msg);
			System.out.println(apply);
		}
	}
	
	public static void main(String[] args) throws Exception {	
		List<String> list = new ArrayList<String>();
		list.add("Wendy");
		list.add("Charlie");
		list.add("lisi");
		list.add("wangwu");
		list.add("zhaoliu");
		list.add("zhaosan");
		
		//    R apply(T t);
		Function<String,Integer> f = (String msg)->{
			return msg.length();
		};
		show(list,f);
	}
}
	测试结果:
5			
7
4
6
7
7
       3) Supplier  接口   
	    抽象方法:	    T get();
	    没有参数
	    返回值类型:	泛型 T

	     功能:提供一个 T (供应者)
	
eg:生成一个8位的随机验证码
        >  使用Random随机生成
public class Test6 {		
	public static void main(String[] args) throws Exception {	
	    Supplier<String> su = () -> {
			//验证码能出现的字符,都在msg中定义
			String msg = "ABCDEFGHHIJKLMNOPQRSTUVXWYZabcdefghijklmnopqrstuvwxyz0123456789";
			StringBuffer sb = new StringBuffer(); //用来生成随机验证码
	        Random r = new Random();    //产生随机数
			for (int i = 0; i < 8; i++) {
				//随机获取msg中某一个位置的字符
				int index = r.nextInt(msg.length());
				sb.append(msg.charAt(index));
			}
			return sb.toString();
		};
		
		System.out.println(su.get());
		System.out.println(su.get());
		System.out.println(su.get());
		System.out.println(su.get());
	}
}
        > 使用UUID类随机生成
//供应一个8位随机码
Supplier<String> s1 = ()->{
	UUID uuid = UUID.randomUUID();
	String msg = uuid.toString().substring(0,8);
	return msg;
};
		
//调用接口中的方法完成功能,获得随机数
System.out.println(s1.get());
System.out.println(s1.get());
System.out.println(s1.get());
System.out.println(s1.get());
System.out.println(s1.get());
    4)Consumer  接口   
	    抽象方法:        void accept(T t); 
	    参数列表:        泛型 T
	    返回值类型:    void 
	     
	     功能:对给定的参数执行此操作
public class Test7 {		

	public static void show(List<String> list,Consumer<String> c) {
		for (String msg : list) {
			//交给接口中的方法进行处理
			c.accept(msg);
		}
	}
	
	public static void main(String[] args) throws Exception {
		List<String> list = new ArrayList<String>();
		list.add("Wendy");
		list.add("Charlie");
		list.add("lisi");
		list.add("wangwu");
		list.add("zhaoliu");
		list.add("zhaosan");
		
		Consumer<String> c = (msg)->{
			System.out.println("操作");
			System.out.println(msg.substring(1));
			System.out.println(msg.toUpperCase());
		};
		
		show(list,c);
		
		//show(list,System.out::println);
	}
}
 5)BiFunction  接口
   	    抽象方法:    	R apply(T t, U u)  
            参数列表:        泛型 T、泛型 U
            返回值类型:    泛型 R   		

            功能:将一个T和一个U输入,返回一个R作为输出


 6)BinaryOperator  接口
            抽象方法:    	T apply(T t, T t)  
            参数列表:        两个相同泛型的 T 
            返回值类型:    泛型 T   		
   		 
   	    功能:将两个T作为输入,返回一个T作为输出
   	
                 BinaryOperator接口继承了BiFunction接口
                 

 7)BiConsumer  接口  
   	    抽象方法:        void accept(T t, U u) 
  	    参数列表:        泛型T、泛型U        
   	    没有返回值
            
            功能:将俩个参数传入,执行操作

4. Java8新特性 之 类型推断

类型推断可以进一步简化你的代码。
Java编译器会从上下文(目标类型)推断出用什么函数式接口来配合Lambda表达式,
这意味着它也可以推断出适合Lambda的签名,因为函数描述符可以通过目标类型来得到。
这样做的好处在于,编译器可以了解Lambda表达式的参数类型,
这样就可以在Lambda语法中省去标注参数类型。

    eg    :
1)在集合中的类型推断
List<String> list = new ArrayList<>();
2)在lambda表达式中的类型推断
public void static void main(String[] args) {
	// R apply(T t);
	Function<String,Boolean> fun = (msg) -> {return msg == "tom"; };
	//会自动推断出 需要传入一个String类型的参数,
	//需要返回一个boolean类型的数据 
}

你可能感兴趣的:(学习笔记,Java8新特性)