Java 8 新特性

Java 8 新特性

Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本。 Oracle 公司于 2014 年 3 月 18 日发布 Java 8 ,它支持函数式编程,新的 JavaScript 引擎,新的日期 API,新的Stream API 等。

Java8 新增了非常多的特性,我们主要讨论以下几个:

  • Lambda 表达式 − Lambda允许把函数作为一个方法的参数(函数作为参数)传递进方法中。
  • 方法引用 − 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
  • 默认方法 − 默认方法就是一个在接口里面有了一个实现的方法。
  • Stream API −新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。
  • Optional 类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。
  • Date Time API − 加强对日期与时间的处理。
  • 新工具 − 新的编译工具,如:Nashorn引擎 jjs、 类依赖分析器jdeps。
  • Nashorn, JavaScript 引擎 − Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用。

更多的新特性可以参阅官网:What’s New in JDK 8


什么是 lambda 表达式

lambda 表达式 本质上是一个匿名方法,是匿名类(函数接口)的一种简单写法。
lambda 表达式 允许把函数作为一个方法的参数(函数作为参数)传递进方法中。

语法

(parameters) -> expression
或
(parameters) -> { statements; }

以下是lambda表达式的重要特征:

  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。

Lambda 表达式实例

	// 1. 不需要参数,返回值为 5  
	() -> 5  
	  
	// 2. 接收一个参数(数字类型),返回其2倍的值  
	x -> 2 * x  
	  
	// 3. 接受2个参数(数字),并返回他们的差值  
	(x, y) -> x – y  
	  
	// 4. 接收2个int型整数,返回他们的和  
	(int x, int y) -> x + y  
	  
	// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
	(String s) -> System.out.print(s)

Lambda 表达式的类型检查、类型推断

Lambda 表达式的类型,叫做“目标类型(target type)”。Lambda 表达式的目标类型是 函数式接口详解,具体的哪个函数式接口需要从上下文推断,例如:

例子 1 :

	List<String> list = new ArrayList<>();
    list.add("lxyng");
    // add(...) 
    list.forEach(s -> System.out.println(s));
    /*
    此处 lambda 表达式的目标类型是:Consumer<T>, 
    Consumer<T> 是一个函数式接口,其中的抽象方法accept(T t),接收一个参数,没有返回值;
    s -> System.out.println(s), 的参数类型是 String, 参数是 s, 主体是 System.out.println(s), 无返回值 void
    */
	
	//为什么? 查看源码可知:
	interface List<E> extends Collection<E>
	
	interface Collection<E> extends Iterable<E> 
	
	public interface Iterable<T> {
		default void forEach(Consumer<? super T> action) { ... }
	}
	
	@FunctionalInterface
	public interface Consumer<T> {
    	void accept(T t);
    	//函数式接口里可以有默认方法,Object 方法,静态方法
		default Consumer<T> andThen(Consumer<? super T> after) {
	        Objects.requireNonNull(after);
	        return (T t) -> { accept(t); after.accept(t); };
	    }
    }

例子 2 :

	// Lambda 表达式
	Thread thread = new Thread(() -> System.out.println("使用 Lambda 表达式创建线路"));

	/*
	() -> System.out.println("使用 Lambda 表达式创建线路") 
	此表达式的目标类型是:Runnable, 没有参数,没有返回值
	*/

	//Thread Runnable 类部分源码
	public class Thread(Runnable runnable){
		...
		public Thread(Runnable target) {
	        init(null, target, "Thread-" + nextThreadNum(), 0);
	    }
	    ...
	}
	@FunctionalInterface
	public interface Runnable {
	    public abstract void run();
	}

你可以用一个 Lambda 表达式为一个函数式接口赋值:
Runnable r1 = () -> {System.out.println("Hello Lambda!");};

然后再赋值给一个Object:
Object obj = r1;

但却不能这样干:
//ERROR! Object is not a functional interface!
Object obj = () -> {System.out.println("Hello Lambda!");};

必须显式的转型成一个函数接口才可以:
Object o = (Runnable) () -> { System.out.println("hi"); };

一个 Lambda 表达式只有在转型成一个函数式接口后才能被当做Object使用。所以下面这句也不能编译:
System.out.println( () -> {} ); //错误! 目标类型不明

必须先转型:
System.out.println( (Runnable)() -> {} ); // 正确

假设你自己写了一个函数接口,长的跟Runnable一模一样:

	@FunctionalInterface
	public interface MyRunnable {
	    public void run();
	}

那么

	Runnable r1 =    () -> {System.out.println("Hello Lambda!");};
	MyRunnable2 r2 = () -> {System.out.println("Hello Lambda!");};

都是正确的写法。这说明一个 Lambda 表达式可以有多个目标类型(函数接口),只要函数匹配成功即可,但需注意一个 Lambda 表达式必须至少有一个目标类型。

JDK预定义了很多函数接口以避免用户重复定义。

	java.lang.Runnable,
	
	java.awt.event.ActionListener, 
	
	java.util.Comparator,
	
	java.util.concurrent.Callable
	
	java.util.function包下的接口,如Consumer、Predicate、Supplier等

最典型的是Function

	@FunctionalInterface
	public interface Function<T, R> {  
	    R apply(T t);
	}

这个接口代表一个函数,接受一个T类型的参数,并返回一个R类型的返回值。

Consumer,跟Function的唯一不同是它没有返回值。

	@FunctionalInterface
    public interface Consumer<T> {
        void accept(T t);
    }

Predicate,用来判断某项条件是否满足。经常用来进行筛滤操作:

	@FunctionalInterface
    public interface Predicate<T> {
        boolean test(T t);
    }

综上所述,一个λ表达式其实就是定义了一个匿名方法,只不过这个方法必须符合至少一个函数接口。

Lambda 表达式的使用

匿名内部类,回调函数等
	/** 创建线路的两种写法 */
	//匿名内部类写法
	Thread myThread = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("使用内部类创建线路");
        }
    });

	// Lambda 表达式
	Thread thread = new Thread(() -> System.out.println("使用 Lambda 表达式创建线路"));
	// 使用匿名内部类  
	Runnable runnable1 = new Runnable() {  
	    @Override  
	    public void run() {  
	        System.out.println("Hello world !");  
	    }  
	};  
	  
	// 使用 lambda 表达式
	Runnable runnable2 = () -> System.out.println("Hello world !");  
	   
	// 直接调用 run 方法(没开新线程哦!)  
	runnable1.run();  
	runnable2.run();  

Thread 类构造方法接收一个 Runnable 接口作为参数,Runnable 是一个函数接口,只有一个 run() 方法,使用 Lambda 表达式比匿名内部类更简洁直观, Lambda 表达式的目标类型是 Runnable, Lambda 表达的目标类型最终是由于调用者决定的,调用者的参数类型决定表达式的目标类型。

Lambda 表达式与集合类批处理操作

集合类的批处理操作。是Java8的另一个重要特性,它与λ表达式的配合使用乃是Java8的最主要特性

 	for(Object o: list) { 
        System.out.println(o);
    }

	//可以写成:
    list.forEach(o -> {System.out.println(o);});	

Java8为集合类引入了另一个重要概念:流(Stream 详解), Lambda 表达式与 Stream 相结合,可以使函数编程式代码更简洁,美观。

	list.forEach(o -> {System.out.println(o);});
	public class Person {  
	  
	private String name;
	private int gender, age;  	//gender 1:男, 0:女
	  
	public Person(String name, int gender, int age)       {  
	          this.name = name;  
	          this.gender = gender;  
	          this.age = age;   
	}  
	// Getter and Setter
}  

List<Person> team1= new ArrayList<Person>() {  
  {  
    add(new Person("Elsdon", 1, 22));  
    add(new Person("Tamsen", 0, 15));  
    add(new Person("Floyd", 1, 33));  
    add(new Person("Sindy", 1, 16));  
    add(new Person("Vere", 0, 44));  
  }  
};  
  
List<Person> team2= new ArrayList<Person>() {  
  {  
    add(new Person("Jarrod", 1, 33));  
    add(new Person("Victor", 0, 18));  
    add(new Person("Tori", 1, 25));  
    add(new Person("Osborne", 0, 10));  
  }  
};  
	System.out.println("所有队员的姓名:");  
	team1.forEach((p) -> System.out.printf("%s %s; ", p.getName(), p.getAge()));  
	team2.forEach((p) -> System.out.printf("%s %s; ", p.getName(), p.getAge()));  
	System.out.println("下面是年龄18岁以上的2位男队员,结果按年龄排序:")  
	team1.stream()  
          .filter((p) -> (p.getAge() > 18))
          .filter(p -> (p.getGender = 1))  
          .limit(2)			//limit(2) 限制结果个数为2, 详细的 Stream<T> API 请看另一篇文章
          .sorted( (p, p2) -> (p.getAge() - p2.getAge()) )  //结果根据年龄排序
          .forEach((p) -> System.out.printf("%s %s; ", p.getName(), p.getAge()));  

Lambda 表达式 与 Stream 流相结合的函数式编程,或者说是流式编程,代码、逻辑都很直观清晰。详细的 Stream 文章请移步流(Stream 详解)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值