Java函数式编程介绍

Java
252
0
0
2023-07-01

Java

介绍Java以后引入的 函数式编程 的各个方面。包括接口函数,Lambda写法,方法引用,组合函数,流式写法等等。

什么是函数式编程?

函数式编程具有以下一些特点:

  • 相同的输入,相同的输出,不管执行多少次
  • 无状态
  • 没有可变量和赋值,没有循环
  • 并行处理友好
  • 没有竞争,不需要同步
  • 只通过返回值通信

Java新的接口

Java8以上版本接口可以这样写

public interface Scalable {
	// 隐式public的抽象方法
	void setScale(double scale);
	//隐式public静态final字段
	double DEFAULT_SCALE =.0;
	//隐式public
	static boolean isScalable(Object obj) {
		return obj instanceof Scalable;
	}
	// 隐式public
	default void resetScale() {
		setScale(DEFAULT_SCALE);
	}
}

接口可以有 静态方法 和默认方法

其中的变量是隐式public和static的,静态方法和默认方法也是隐式public的。

应用到新版 JDK 中的Comparator中:

import java.util.Comparator;
public class Interfaces {
	public static void main(String[] args) {
		
		Comparator<String> byName = new Comparator<String>() {
			public int compare(String a, String b) {
				return a.compareTo(b);
			}
		};
		System.out.println(byName.compare("i", "k"));
		try {
			System.out.println(byName.compare("s", null));
		} catch (NullPointerException e) {
			System.out.println(e);
		}
		// Comparator中的静态方法
		Comparator<String> byStringThenNull = Comparator.nullsLast(byName);
		System.out.println("Then null:");
		System.out.println(byStringThenNull.compare("d", "w"));
		System.out.println(byStringThenNull.compare("g", null));
		// Comparator中的默认方法
		Comparator<String> nullThenByDecreasingString= byStringThenNull.reversed();
		System.out.println("Reversed:");
		System.out.println(nullThenByDecreasingString.compare("ww", "hh"));
		System.out.println(nullThenByDecreasingString.compare("fv", null));
	}

函数式接口 function al Interface

函数式接口的特点

  • 具有唯一一个抽象方法的接口就是函数式接口
  • 可以有多个静态方法或者默认方法
  • 以前版本的JDK就有这样的接口(Runnable,Comparable,Comparator,Iterable)
  • 函数式接口是用来被无状态的类实现
  • 函数式接口在流式( Stream s)写法中扮演重要角色
  • @FunctionnalInterface注释标志 纯粹 的函数式接口
  • 在Java9中有40多个纯函数式接口,位于包java.util.function中

函数式接口分类一览

JDK中的Consumer函数接口:

package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Consumer<T> {
 void  accept (T var1);
 default Consumer<T> andThen(Consumer<? super T> var) {
 Objects.requireNonNull(var);
 return (var) -> {
 this.accept(var);
 var.accept(var2);
 };
 }
}

Lambda

  • Lambda是一种实现函数式接口的简写方式,不再使用匿名类的方式
  • 写法简洁
  • 不会产生额外的类文件,匿名类会编译成额外的class文件
  • 只能在函数式接口中使用

示例 :

import java.util.Comparator;
import java.util.function.Consumer;
public class Lambda {
	public static void main(String[] args) {
		// 老式写法
		Comparator<String> by length  = new Comparator<String>() {
			public int compare(String a, String b) {
				return  Integer .compare(a.length(), b.length());
			}
		};
		// lambda表达式
		Comparator<String> byLengthLambda = (String a, String b) -> {
			return Integer.compare(a.length(), b.length());
		};
		// 移除参数类型
		Comparator<String> byLengthLambda = (a, b) -> {
			return Integer.compare(a.length(), b.length());
		};
		// 只有单行,所以可以移除花括号和return
		Comparator<String> byLengthLambda = (a, b) -> Integer.compare(a.length(), b.length());
		
		//没有参数的写法
		Runnable r = ()->System.out.println("A compact runnable.");
		Thread t= new Thread(r);
		
		//不需要显示定义runnable
		Thread t=new Thread(()->System.out.println("A compact runnable."));
		
		//只有一个参数,可以去掉小括号
		Consumer<String> lengthPrinter = s -> System.out.println(s.length()); 

 // 编译器足够聪明,虽然循环次,但只生成一个Consumer实例
		for (int i=; i<5; i++) {
			Consumer<String> myPrinter =
					msg -> System.out.println("Consuming " + msg);
			myPrinter.accept(myPrinter.toString());
		}
 	}
 }

方法引用

  • Java8以后提供一种方法引用而不是执行的机制
  • 像C/C++中的函数指针
  • 比反射机制更高效

示例:

import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public class MethodReferences {
	interface ThreadSupplier {
		Thread giveMeAThread();
	}
	@SuppressWarnings("unused")
	public static void main(String[] args) {
		// 静态方法引用
		Supplier s = Thread::currentThread;
		ThreadSupplier ts = Thread::currentThread;
		// 实例方法引用
		Employee frank = new Employee("Frank",);
		Supplier<Integer> s = frank::getSalary;
		System.out.println(s.get());
		Consumer<String> c = System.out::println;
		// 实例方法,但不用指定实例
		Function<Employee, Integer> f = Employee::getSalary;
		Integer frankSalary = f.apply(frank);
	}
}

组合函数

import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.function.Consumer;
import java.util.function.Function;
public class ComposingFunctions {
	public static void main(String[] args) throws FileNotFoundException {
		PrintWriter writer = new PrintWriter("test.txt");
		Consumer<String> logger = writer::println;
		Consumer<String> screener = System.out::println;
		Consumer<String> both = logger.andThen(screener);
		both.accept("Test composing functions.");
		writer.close();
		
		Employee john=new Employee("John",);
		Function<Employee, String> getName = Employee::getName;
		Function<String,Character> getFirstLetter =name->name.charAt();
		//andThen是从左到右
		Function<Employee,Character> bothFunction=getName.andThen(getFirstLetter);
		bothFunction.apply(john);
		//compose是从右到左
		Function<Employee,Character> botFounction=getFirstLetter.compose(getName);
		botFounction.apply(john);
			
	}
}

Streams

Java中的流有以下特性:

  • 流用来处理序列数据
  • 内置迭代器,对所有数据应用相同的操作进行处理
  • 接口java.util.Stream <T> //有30多个实例方法,7个静态方法
  • Stream可以是有序或无序的
  • Stream可以是串行处理或并行处理
  • Stream只能遍历一次

创建stream的几种方法

  • 静态方法Stream.of(1,2,4);
  • 通过数组创建Arrays.stream(names)
  • 从集合collection创建stream
  • 通过计算获得stream Stream.generate() Stream.iterate()

Stream的操作

  • 过滤
  • 基于内容 filter,takeWhile,dropWhile
  • 数量过滤 limit
  • 唯一性过滤 distinct
import java.util.Random;
import java.util.stream.Stream;
public class FilterStream {
	public static void main(String[] args) {
		//打印个大于0的随机整数
		final Random random = new Random();
		Stream<Integer> randoms = Stream.generate(random::nextInt);
		randoms.filter(n->n>)
		.distinct()
		.limit()
		.forEach(System.out::println);
	}
}

  • 变换
  • 变换类型
  • 排序
  • 不排序
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Stream;
public class TransformStream {
	public static void main(String[] args) {
		//按工资降序打印个雇员的名字
		List<Employee> listEmployee = new ArrayList<>();
		listEmployee.add(new Employee("Mike",));
		listEmployee.add(new Employee("John",));
		Stream<Employee> emps = listEmployee.stream();
		emps.sorted(Comparator.comparingInt(Employee::getSalary).reversed())
		 .limit()
		 .map(Employee::getName)
			.forEachOrdered(System.out::println);
	}
}

  • 终止操作
  • 提取单个元素 findAny findFirst min max
  • 所有数据放到一个数组 toArray
  • 计数 count
  • 条件判断 allMatch anyMath noneMatch
  • 针对每调数据执行别的操作

原始类型的Stream

 List<Employee> listEmployee = new ArrayList<>();
 listEmployee.add(new Employee("Mike",));
 listEmployee.add(new Employee("John",));
 Stream<Employee> emps = listEmployee.stream();
 OptionalDouble avgSalary = emps.mapToInt(Employee::getSalary).average();

并行处理

需要避免有状态对象,否则使用并行处理有可能出错或者速度很慢

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class ParallelDemo {
	public static void main(String[] args) {
		List<Employee> listEmployee = new ArrayList<>();
		listEmployee.add(new Employee("Mike",));
		listEmployee.add(new Employee("John",));
		listEmployee.add(new Employee("Alice",));
		listEmployee.add(new Employee("Bob",));
		listEmployee.add(new Employee("Dick",));
		Stream<Employee> emps = listEmployee.stream();
		emps.parallel().map(Employee::getName).forEach(System.out::println);
	}
}

函数式的思考方式

使用不变对象 :

  • 使用final field
  • 所有reference fields都指向不变对象,比如string,integer
  • 当需要更新对象的时候,不是更新,而是新建一个对象

使用无状态函数

  • 避免修改实例属性
  • 避免修改静态属性
  • 避免修改参数状态
  • 同样的参数返回的结果总是一样