Java 杂谈Java

函数式编程

2019-07-24  本文已影响0人  5fafe9a996b9

函数式编程之前我们关注的往往是某一类对象应该具有什么样的属性,当然这也是面向对象的核心--对数据进行抽象。但似乎在某种场景下,更加关注某一类共有的行为描述(这似乎与之前的接口有些类似),这也就是提出函数式编程的目的。java8通过函数式接口和Lambda支持函数式编程。
函数式编程可简单理解是把函数的实现作为函数的参数进行传递

函数式接口

函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。

函数式接口可以被隐式转换为 lambda 表达式,JAVA 8 之前一般是用匿名类实现的,当然也可以显示继承实现。

例如:定义一个函数式接口GreetingService

@FunctionalInterface
public interface  GreetingService {
  void sayMessage(String message);
}

函数式接口GreetingService的使用

  public static void main(String[] args) throws Exception {
    //lamda表达式实现
    hello("Andy",message -> System.out.println("Hello " + message));
    
    //匿名类实现
    hello("John",new GreetingService(){
      @Override
      public void sayMessage(String message) {
        System.out.println("你好 " + message);
      }
    });

    //继承实现
    GreetingService greetingService=new GreetingServiceImpl();
    hello("Peter",greetingService);
  }
  
  public static void hello(String name,GreetingService greetingService ) {
    greetingService.sayMessage(name);
  }

输出结果:

Hello Andy
你好 John
早上好 Peter

JDK预先定义了一些函数式接口:


image

示例

 //Consumer<T> 消费型接口 :
 @Test
 public void test1(){
  happy(10000, (m) -> System.out.println("你们刚哥喜欢大宝剑,每次消费:" + m + "元"));
 }
 public void happy(double money, Consumer<Double> con){
  con.accept(money);
 }

其他接口(一部分):


image

方法引用和构造器引用

方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法。方法引用提供了一种引用而不执行方法的方式,它需要由兼容的函数式接口构成的目标类型上下文。计算时,方法引用会创建函数式接口的一个实例,方法引用使用一对冒号 ::。

当Lambda表达式中只是执行一个方法调用时,不用Lambda表达式,直接通过方法引用的形式可读性更高一些。方法引用是一种更简洁易懂的Lambda表达式。

实例:

public class Car {
  //Supplier是jdk1.8的接口,这里和lamda一起使用了
  public static Car create(final Supplier<Car> supplier) {
    return supplier.get();
  }

  public static void collide(final Car car) {
    System.out.println("Collided " + car.toString());
  }

  public void follow(final Car another) {
    System.out.println("Following the " + another.toString());
  }

  public void repair() {
    System.out.println("Repaired " + this.toString());
  }
}
 public static void main(String[] args) throws Exception {
    //构造器引用Car::new
    final Car car = Car.create( Car::new );
    final List< Car > cars = Arrays.asList( car );

    //静态方法引用Car::collide
    cars.forEach( Car::collide );

    //特定类的任意对象的方法引用Car::repair
    cars.forEach( Car::repair );

    final Car police = Car.create( Car::new );

    //特定对象的方法引用police::follow
    cars.forEach( police::follow );
  }

结果

Collided cn.org.geneplus.data.CarTT.Car@f2a0b8e
Repaired cn.org.geneplus.data.CarTT.Car@f2a0b8e
Following the cn.org.geneplus.data.CarTT.Car@f2a0b8e

Lambda表达式

Lambda表达式在Java 语言中引入了一个新的语法元素和操作符。这个操作符为 “->” , 该操作符被称为 Lambda 操作符或剪头操作符。它将 Lambda 分为两个部分:
左侧: 指定了 Lambda 表达式需要的所有参数
右侧: 指定了 Lambda 体,即 Lambda 表达式要执行的功能。

Comparator<Integer> com = (x, y) -> {
 System.out.println("函数式接口");
 return Integer.compare(x, y);
};

Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出数据类型,即“类型推断”

类型推断:Lambda 表达式中的参数类型都是由编译器推断 得出的。 Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。 Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的“类型推断”

Lambda 是一个匿名函数,我们可以把 Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
Lambda表达式的关键:从匿名类到 Lambda 的转换

image

Lambda与Lambda外变量

Lambda表达式只能引用标记了 final的外层局部变量,这个final可以即使不显示标记也会隐式的自动标记,因此final使用了的局部变量在后续不能修改。
在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量。

上一篇下一篇

猜你喜欢

热点阅读