JDK8特性函数式接口与Stram流

JDK8特性函数式接口与Stram流

函数式接口

函数式接口,一般java中一个接口中只有一个方法就可以被认为是函数式接口,用于契合jdk8中的lamda的
在jdk用也有很多函数式接口,比如典型的Runable和Comparable等接口都是函数式接口
jdk1.8中约定函数式接口的接口上要加@FunctionalInterface注解,这个不是必须的,Comparable就没有这个注解,这个注解的意义只起约定作用,如果你在设计接口时候加上这个注解,倘若你要新加方法,这个注解就会爆红,表示这是一个函数式约定的接口,你的方法数量不能不等于1
开发中加入这个注解的意义就是提醒开发者,不要去给这个接口添加方法了

一、JDK8中的新函数式接口

为了方便之后的Stream流的使用,jdk8是新加了4个函数式接口,灵活使用这四个接口将会简化开发,美化代码,不得不说官方的设计给我们了不少设计上的启发

生产接口Supplier

java.util.function.Supplier< T >
接口包含一个无参的方法:T get();用来获取一个泛型参数指定类型的对象数据
这个接口也被称之为生产型接口,指定接口的泛型是什么,就返回什么类型的数据
泛型中可以指定需要生产的类型

public class Demo01Supplier{
    
    
	public static void main(String[] args) {
    
    
		System.out.println(getSupplier(()->"我自己"));//这里生产了一个字符串
	}
	public static String getSupplier(Supplier<String> supplier) {
    
    
		return supplier.get();
	}
}
消费接口Consumer

java.util.function.Consumer< T >
接口包含一个默认方法和一个accept(T t)方法,accept是用来消费传入的规定泛型的对象
这个接口也被称之为消费型接口,指定一个泛型,自定义处理这个泛型对象的逻辑,来消费这个对象
Consumer的默认方法:AndThen 连接两个消费式接口,可以连接很多个
例:
Consumer< String > c1;
Consumer< String > c2;
c1.AndThen(c2).accept(String);
先执行前面的消费式接口

public class Demo02Consumer {
    
    
	public static void main(String[] args) {
    
    
		acceptConsumer("迪丽热巴", (name)->System.out.println(new StringBuilder(name).reverse().toString()));//将输入的字符串反转然后打印到控制台
		AndThenConsumer("迪丽热巴",(name)->System.out.println(name)
				,(name)->System.out.println(new StringBuilder(name).reverse().toString()));//将输入的字符串先打印一边,然后再反转再打印一遍
		String[] arr= {
    
    "迪丽热巴,女","古力娜扎,女","马尔扎哈,男"};
		AndThenConsumer(arr,(string)->{
    
    
			String ar=string.split(",")[0];
			System.out.print("姓名:"+ar+"	");
		},(string)->{
    
    
			String ar=string.split(",")[1];
			System.out.println("性别:"+ar);
		});//两次处理后 输出的样子是 姓名:迪丽热巴 &nbsp; 性别:女,然后一行一行打印
	}
	public static void acceptConsumer(String name,Consumer<String> con) {
    
    //对字符串做消费
		con.accept(name);
	}
	public static void AndThenConsumer(String name,Consumer<String> c1,Consumer<String> c2) {
    
    //对字符串做两次消费
		c1.andThen(c2).accept(name);
	}
	public static void AndThenConsumer(String[] name,Consumer<String> c1,Consumer<String> c2) {
    
    //对字符串数组做两次消费
		for (String string : name) {
    
    
			c1.andThen(c2).accept(string);
		}
	}
}
断言接口Predicate

java.util.function.Predicate< T >
作用:对某种数据类型的数据进行判断,结果返回一个布尔值
这个接口被叫做断言式接口,主要用于对对象的筛选判断,形式类似于junit中的断言
接口中的一个抽象方法:
boolean test(T t)
符合返回true
不符合返回false
接口中的一个默认方法:
and
p1.and(p2).test(T t);
相当于&&,同时满足返回true,可多个and
接口中的另一个默认方法:
p1.or(p2).test(T t);
相当于||,同时不满足返回false
这里就不代码实现了!
接口中的最后一个默认方法:
p1.negate().test(T t);
相当与!,就是非的意思,不满足就返回true,注意!这里并不是两个断言
接口中还有一个静态方法:
isEqual

public class Demo03Predicate {
    
    
	public static void main(String[] args) {
    
    
		String ar="abcdfe";
		System.out.println(TestPredicate(ar, (str)->str.length()>=6));//字符串长度是否大于等于6   
		System.out.println(AndTestPredicate(ar, (str)->str.length()>3,(str)->str.contains("a")));//判断长度大于3且包含字符串"a"
		System.out.println(NegateTestPredicate(ar,(str)->str.length()>3));//长度大于3返回false

	}
	public static boolean TestPredicate(String str,Predicate<String> pre) {
    
    //test
		return pre.test(str);
	}
	public static boolean AndTestPredicate(String str,Predicate<String> p1,Predicate<String> p2) {
    
    //and
		return p1.and(p2).test(str);
	}
	public static boolean NegateTestPredicate(String str,Predicate<String> pre) {
    
    //negate
		return pre.negate().test(str);
	}
}
转换类型接口Function

java.util.function< T , R >
这个接口用来根据一个类型的数据得到另一个类型的数据。前者为前置条件,后者为后置条件
Function中的最重要的抽象方法为:
R apply(T t),根据T类型的参数获取类型R的结果
使用场景例如:
将String类型转换成Integer类型
Function中的默认方法为
f1.AndThen(f2).apply(T t)

Function中的默认方法identity,用来查看原来是什么类型的

public class Demo04Function {
    
    
	public static void main(String[] args) {
    
    
		String b="赵丽颖,20";
		String a = "123456";
		System.out.println(ApplyFunction(a, (s) -> Integer.parseInt(s)));
		/*将String转成Integer然后给数值减10000再转换回来*/
		System.out.println(AndThenApplyFunction(a,(s)->Integer.parseInt(s)-10000,(s)->String.valueOf(s)));
		/*将字符串b分割,取后面的20转成Integer,然后在返回其-2*/
		System.out.println(FT(b,(s)->b.split(",")[1],(s)->Integer.parseInt(s),(s)->s-2));
	}
	public static Integer ApplyFunction(String s, Function<String, Integer> f) {
    
    
		return f.apply(s);
	}
	public static String AndThenApplyFunction(String s, Function<String, Integer> f1, Function<Integer, String> f2) {
    
    
		return f1.andThen(f2).apply(s);
	}
	public static Integer FT(String s,Function<String,String> f1,Function<String,Integer> f2,Function<Integer,Integer> f3) {
    
    //注意每一个Function的泛型
		return f1.andThen(f2.andThen(f3)).apply(s);
	}
}

二、Stream流

Stream流属于管道流,用一次就关闭,所以没必要用对象接收流

Stream流中的终结方法:所有方法最后都要以终结方法作为结尾
		forEach(Consumer(<? super T> action);
		long count();这也是终结方法,返回元素个数
Stream流中的过滤方法:
		filter(Predicate(<? super T> predicate));
		使用Predicate中的方法test判断筛选过滤
Stream流中的映射方法:
		map(Function<T,R> fun);
		将一个一种类型转换成另一种类型数据
Stream流中截取前n个元素的方法:
		Stream<T> limit(long l);
Stream流中的跳过方法:
		Stream<T> skip(long l);
		跳过前l个元素,将后面的元素创建一个新的Stream流
Stream流中的合并方法:
		Stream<T> Stream.concat(Stream<? extends T> s1,Stream<? extends T> s2)

注意:一下代码中的of作用是返回其元素为指定值的顺序有序流对流进行操作

@SafeVarargs
@SuppressWarnings("varargs") //从arrays创建一个流是安全的
public static<T> Stream<T> of(T... values) {
     
     
 return Arrays.stream(values);
}

我们在使用stream流时也不用每次都of一个对象,要注意下面两点

  • Collection< T >集合中有.stream方法,可以创建一个Stream流对象
  • Map< T , T >集合中没有.stream方法,需要间接导入
public class StreamInMethod {
    
    
	public static void main(String[] args) {
    
    
		Stream.of("张三","李四","王五","赵六").forEach(s->System.out.print(s+"	"));//forEach终结方法
		System.out.println();
		Stream.of("张三","李四","王五","赵六").filter(s->s.startsWith("张"))//filter过滤方法
				.forEach(s->System.out.println(s));
		Stream.of("1","2","3","4").map(s->Integer.parseInt(s))//map映射方法
				.forEach(s->System.out.print(s+"	"));
		System.out.println();
		System.out.println(Stream.of("1","2","3","4").count());//count终结方法,返回long
		Stream.of("1","2","3","4").limit(2).forEach(s->System.out.print(s+"	"));//limit截取方法
		System.out.println();
		Stream.of("1","2","3","4").skip(2).forEach(s->System.out.print(s+"	"));//skip跳过方法
		System.out.println();
		Stream.concat(Stream.of("我","你"), Stream.of("1","2")).forEach(s->System.out.print(s+"	"));//concat合并方法
	}
}

个人想法

我感觉在使用这些函数式接口的连用方法时,我们的需求是需要一个流程化的操作,如果中途出现了异常,那可能将会对原有数据进行改动,并且并没有完成剩余逻辑,导致出现问题,这样的方法执行是不满足原子性的(看你的需求),当我们要解决这种中途exception时候,只能在流程中对参数操作的每一步进行筛选判断,或者设置回滚策略,设计的时候也要多考虑一下这样的可能性

猜你喜欢

转载自blog.csdn.net/qq_44769485/article/details/114468975