天天看点

Java常用的函数式编程

lambda表达式是函数式编程的核心,即隐式函数,没有函数名,只有函数体。

lambda表达式特点

(1)代码简单

(2) 对象不变性

lambda表达式的方法引用

(1)静态方法:ClassName::methodName

(2)实例方法:instance::methodName

(3)超类的实例方法:super::methodName

(4)类型上的类型引用:ClassName::methodName

(5)构造方法引用:Class::new

(6)数组构造方法引用:TypeName[]::new

重点:通过@FunctionalInterface类型接口,定义构造函数:

class User{
    private int id;
    private String name;
    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }
    public String getName() {
        return name;
    }
}
class ConstrMethodRef{
    // 通过 User 的工厂类 UserFactory(函数式接口),使用User::new创建接口实例时,
    // 系统会根据 UserFacroty.create() 的函数签名选择合适的 User 构造函数
    @FunctionalInterface
    interface UserFactory<U extends User>{
        // 函数式接口定义:只有一个抽象方法;其它(静态、默认)方法不做限制
        U create(int id, String name);
    }
    static UserFactory<User> userRef = User::new;
    public static void main(String[] args) {
        List<User> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(userRef.create(i, "xiao" + Integer.toString(i)));
        }
        list.stream().map(User::getName).forEach(System.out::print);
    }
}
           

第二次结果还是原数组,说明函数式编程的不变性(所以函数式并行编程不用考虑线程安全性问题,因为每个对象都是不变的);

int[] nums = {1, 2, 3, 4};
String[] strs = {"xsx", "csw", "dwfwfve", "efwe", "frwqweqa"};

// 1.将所有元素 +1,并打印;
Arrays.stream(nums).map((x) -> x = x + 1).forEach(System.out::print); // {2,3,4,5}

// 2.传入的还是 nums,即使 上一条语句 执行过了,所以打印的还是原数组
Arrays.stream(nums).forEach(System.out :: print); // {1,2,3,4}

// 3.元素为奇数 则加1,打印
Arrays.stream(nums).map(x -> (x % 2 == 0 ? x : x + 1)).forEach(System.out::println);

// 4.排序时的比较器(对strs字符串数组排序,规则:先按字符串长度升序,再按大小写不敏感的字母顺序排序)
Arrays.sort(strs, Comparator.comparingInt(String::length).thenComparing(String.CASE_INSENSITIVE_ORDER));

// 5. 
 
           

流式编程的演变:

// 1.通过临时实例化一个 Consumer 对象,重写其中的方法,实现流式编程
Arrays.stream(strs).forEach(new Consumer<String>() {
    @Override
    public void accept(String s) {
        System.out.println(s);
    }
});

// 2.推断Consumer接口,省略Consumer实例
Arrays.stream(strs).forEach((final String x) -> { System.out.println(x); });

// 3.省略 参数类型、final 和 大括号;
Arrays.stream(strs).forEach((x) ->  System.out.println(x));

// 4.可以使用方法引用的,直接使用方法引用
Arrays.stream(strs).forEach( System.out::println);

// 5. 使用
  // 通过 Consumer 定义两个方法引用
  IntConsumer outPrint = System.out::println;
  IntConsumer errPrint = System.err::println;
  // 直接在stream中使用定义的方法引用
  Arrays.stream(nums).forEach(outPrint.andThen(errPrint));
           

并行流

// 1.获取指定范围内的质数总数
IntStream.range(1, 10000).parallel().filter(PrimeUtil::isPrime).count();

// 2.从集合得到并行流
double avg = list.parallelStream().mapToInt(s -> s.id).average().getAsDouble();

// 3.并行排序
Arrays.parallelSort(nums);

// 4.并行赋值
Arrays.parallelSetAll(nums, (i) -> new Random().nextInt());
           

lambda表达式实现

lambda表达式的实现类似于匿名类的实现,只是写法上有所区别,通过编译反编译lambda表达式,可以清楚看到。

问题:如何编译、反编译、查看lambda表达式的实现方式?