您的位置 首页 java

Java函数式编程介绍

Java

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

什么是函数式编程?

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

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

Java新的接口

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

public interface Scalable {
// 隐式public的抽象方法
void setScale(double scale);
//隐式public静态final字段
double DEFAULT_SCALE = 1.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> var1) {
 Objects.requireNonNull(var1);
 return (var2) -> {
 this.accept(var2);
 var1.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> byLengthLambda1 = (String a, String b) -> {
return Integer.compare(a.length(), b.length());
};
// 移除参数类型
Comparator<String> byLengthLambda2 = (a, b) -> {
return Integer.compare(a.length(), b.length());
};
// 只有单行,所以可以移除花括号和return
Comparator<String> byLengthLambda3 = (a, b) -> Integer.compare(a.length(), b.length());

//没有参数的写法
Runnable r = ()->System.out.println("A compact runnable.");
Thread t= new Thread(r);

//不需要显示定义runnable
Thread t1=new Thread(()->System.out.println("A compact runnable."));

//只有一个参数,可以去掉小括号
Consumer<String> lengthPrinter = s -> System.out.println(s.length()); 

 // 编译器足够聪明,虽然循环5次,但只生成一个Consumer实例
for (int i=0; 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 MethodReferences2 {
interface ThreadSupplier {
Thread giveMeAThread();
}
@SuppressWarnings("unused")
public static void main(String[] args) {
// 静态方法引用
Supplier s1 = Thread::currentThread;
ThreadSupplier ts = Thread::currentThread;
// 实例方法引用
Employee frank = new Employee("Frank", 3000);
Supplier<Integer> s2 = frank::getSalary;
System.out.println(s2.get());
Consumer<String> c1 = System.out::println;
// 实例方法,但不用指定实例
Function<Employee, Integer> f1 = Employee::getSalary;
Integer frankSalary = f1.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", 5000);
Function<Employee, String> getName = Employee::getName;
Function<String,Character> getFirstLetter =name->name.charAt(0);
//andThen是从左到右
Function<Employee,Character> bothFunction1=getName.andThen(getFirstLetter);
bothFunction1.apply(john);
//compose是从右到左
Function<Employee,Character> botFounction2=getFirstLetter.compose(getName);
botFounction2.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) {
//打印10个大于0的随机整数
final Random random = new Random();
Stream<Integer> randoms = Stream.generate(random::nextInt);
randoms.filter(n->n>0)
.distinct()
.limit(10)
.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) {
//按工资降序打印10个雇员的名字
List<Employee> listEmployee = new ArrayList<>();
listEmployee.add(new Employee("Mike", 6000));
listEmployee.add(new Employee("John", 7000));
Stream<Employee> emps = listEmployee.stream();
emps.sorted(Comparator.comparingInt(Employee::getSalary).reversed())
 .limit(10)
 .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", 6000));
 listEmployee.add(new Employee("John", 7000));
 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", 6000));
listEmployee.add(new Employee("John", 7000));
listEmployee.add(new Employee("Alice", 7000));
listEmployee.add(new Employee("Bob", 7000));
listEmployee.add(new Employee("Dick", 7000));
Stream<Employee> emps = listEmployee.stream();
emps.parallel().map(Employee::getName).forEach(System.out::println);
}
}

 

函数式的思考方式

使用不变对象 :

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

使用无状态函数

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

文章来源:智云一二三科技

文章标题:Java函数式编程介绍

文章地址:https://www.zhihuclub.com/194331.shtml

关于作者: 智云科技

热门文章

网站地图