JDK1.8新特性——接口改动和Lambda表达式

摘要:本文主要学习了JDK1.8的新特性中有关接口和Lambda表达式的部分。

部分内容来自以下博客:

https://www.cnblogs.com/onetwo/p/8526374.html

https://www.cnblogs.com/xxez-d/p/5989944.html

https://www.cnblogs.com/runningTurtle/p/7092632.html

https://www.cnblogs.com/jiangwz/p/9197238.html

https://www.cnblogs.com/hujingnb/p/10181630.html

接口增强

在JDK1.8以前的版本中,定义一个接口时,所有的方法必须是抽象方法,不能有具体实现,这是Java语法规定的。但是在JDK1.8中定义一个接口时,在满足特定的前提下,可以有方法的具体实现。这样一个接口中可以有属性,可以有抽象方法,也可以有具体的方法,这跟JDK1.8以前的接口比,明显接口的功能变得强大了。

使用接口增强

接口中定义具体的方法实现是有限制的,它不能像我们在一个普通类那样随便定义方法实现,它只能定义default和static类型的方法。

在调用的时候,被default修饰的默认方法需要通过实现了该接口的类去调用,被static修饰的静态方法可以通过接口名直接调用。

default和static不能用来修饰Object中的public方法,但是可以修饰Object中其他访问修饰符修饰的方法。

代码如下:

 public class Demo {
public static void main(String[] args) {
NewInterface.testStatic();
new NewInterfaceImpl().testDefault();
}
} interface NewInterface {
public String name = ""; default void testDefault() {
System.out.println("测试新增的default方法");
} static void testStatic() {
System.out.println("测试新增的static方法");
}
} class NewInterfaceImpl implements NewInterface {
@Override
public void testDefault() {
NewInterface.super.testDefault();
System.out.println("测试子类重写的新增的static方法");
}
}

运行结果如下:

 测试新增的static方法
测试新增的default方法
测试子类重写的新增的static方法

好处

想象这样一中情况,当多个类实现一个接口的某个方法时,方法的具体实现代码相同,这样就会造成代码重复问题。接口增强就相当于把公共的代码抽离出来,放入接口定义中,这样实现类对于该方法就不用重新定义,直接调用即可,这很好的解决了实现该接口的子类代码重复的问题。

函数式接口

所谓的函数式接口,当然首先是一个接口,然后就是在这个接口里面只能有一个抽象方法,这种类型的接口也称为SAM(Single Abstract Method)接口。

下面的代码展示了一个简单的函数式接口:

 interface NewInterface {
void test();
}

关于@FunctionalInterface注解

JDK1.8为函数式接口引入了一个新注解@FunctionalInterface,主要用于编译级错误检查,加上该注解,当你写的接口不符合函数式接口定义的时候,编译器会报错。

加不加@FunctionalInterface对于接口是不是函数式接口没有影响,该注解只是提醒编译器去检查该接口是否仅包含一个抽象方法。

下面的代码展示了一个加了注解的函数式接口:

 @FunctionalInterface
interface NewInterface {
void test();
}

函数式接口里允许定义默认方法

函数式接口里是可以包含默认方法,因为默认方法不是抽象方法,其有一个默认实现,所以是符合函数式接口的定义的。

下面的代码展示了一个带有默认方法的函数式接口:

 @FunctionalInterface
interface NewInterface {
void test(); default void defaultRead() {
System.out.println("defaultRead() ...");
} default void defaultWrite() {
System.out.println("defaultWrite() ...");
}
}

函数式接口里允许定义静态方法

函数式接口里是可以包含静态方法,因为静态方法不能是抽象方法,是一个已经实现了的方法,所以是符合函数式接口的定义的。

下面的代码展示了一个带有静态方法的函数式接口:

 @FunctionalInterface
interface NewInterface {
void test(); static void staticRead() {
System.out.println("staticRead() ...");
} static void staticWrite() {
System.out.println("staticWrite() ...");
}
}

函数式接口里允许定义java.lang.Object里的public方法

函数式接口里是可以包含Object里的public方法,这些方法对于函数式接口来说,不被当成是抽象方法(虽然它们是抽象方法)。因为此接口的实现类一定是Object的子类,会自动实现这个接口中定义的抽象方法。

但也只能是public方法,其他访问修饰符修饰的方法是不允许在函数式接口里定义的。

下面的代码展示了一个带有Object类的public方法的函数式接口:

 @FunctionalInterface
interface NewInterface {
void test(); @Override
public String toString(); @Override
public boolean equals(Object obj); // @Override
// public Object clone();
}

JDK1.8新增的函数式接口

JDK1.8内置的函数式接口放在包java.util.function下,默认在JDK安装路径下的src.zip中。

Supplier接口

Supplier接口没有参数,只有返回值,作用是为了产生对象。

 @FunctionalInterface
public interface Supplier<T> {
T get();
}

Consumer接口

Consumer接口有参数,但是没有返回值,作用是为了进行某种操作,比如打印操作。

 @FunctionalInterface
public interface Consumer<T> {
void accept(T t); default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}

Function接口

Function接口有参数,也有返回值,作用是对参数进行处理并返回。

 @FunctionalInterface
public interface Function<T, R> {
R apply(T t); default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
} default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
} static <T> Function<T, T> identity() {
return t -> t;
}
}

Predicate接口

Predicate接口有参数,也有返回值,但返回值是Boolean类型的值,作用是对传入的参数进行某种判断。

 @FunctionalInterface
public interface Predicate<T> {
boolean test(T t); default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
} default Predicate<T> negate() {
return (t) -> !test(t);
} default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
} static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}

Lambda表达式

什么是Lambda表达式

通过使用表达式来提供一种清晰简洁的方式来表示方法接口。

语法

参数列表 -> 方法体

如果参数列表有多个值,可以使用小括号包含,如果只有一条,则可以省略小括号。

如果方法体有多条语句,可以使用大括号包含,如果只有一条,则可以省略大括号,如果是return语句,则可以省略return关键字。

使用举例

在创建一个线程并调用的时候,需要传入一个Runnable接口的实现类。

使用Lambda表达式之前:

 new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread1 run() ...");
}
}).start();

使用Lambda表达式之后:

 new Thread(() -> System.out.println("Thread2 run() ...")).start();

变量作用域

Lambda表达式只能引用标记了final的外层局部变量,这就是说不能在Lambda内部修改定义在域外的局部变量,否则会编译错误。

 final int num = 100;
NewInterface ni = i -> String.valueOf(i + num);
System.out.println(ni.test(num));

Lambda表达式的局部变量可以不用声明为final,但是必须不可被后面的代码修改(即隐性的具有final的语义)。

 final int num = 100;
NewInterface ni = i -> String.valueOf(i + num);
System.out.println(ni.test(num));
// num++;

在Lambda表达式当中不允许声明一个与局部变量同名的参数或者局部变量。

 final int num = 100;
// NewInterface ni = num -> String.valueOf(num);
NewInterface ni = i -> String.valueOf(i);
System.out.println(ni.test(num));

方法引用

什么是方法引用

方法引用是JDK1.8中提出的用来简化Lambda表达式的一种手段。它通过类名和方法名来定位到一个静态方法或者实例方法。

语法

类或对象名::方法名或new

静态方法引用

如果是静态方法引用,那么语法是 类名::静态方法名 ,并且方法调用传入的参数就是方法用到的参数。

代码如下:

 public class Demo {
public static void main(String[] args) {
// 使用匿名内部类
// NewInterface ni = new NewInterface() {
// @Override
// public String test(String name) {
// return String.valueOf(name);
// }
// };
// 使用Lambda表达式
// NewInterface ni = name -> String.valueOf(name);
// 使用方法引用
NewInterface ni = String::valueOf;
System.out.println(ni.test("name"));
}
} @FunctionalInterface
interface NewInterface {
String test(String name);
}

运行结果如下:

 name

实例方法引用

如果是实例方法引用,那么语法是 对象实例::方法名 ,并且方法调用传入的第一个参数是方法的调用者,后面的参数才是方法的参数。

代码如下:

 public class Demo {
public static void main(String[] args) {
// 使用匿名内部类
// NewInterface ni = new NewInterface() {
// @Override
// public int test(String str) {
// return str.length();
// }
// };
// 使用Lambda表达式
// NewInterface ni = str -> str.length();
// 使用方法引用
NewInterface ni = String::length;
System.out.println(ni.test("lenth"));
}
} @FunctionalInterface
interface NewInterface {
int test(String str);
}

运行结果如下:

 5

构造方法引用

如果是构造方法引用,那么语法是 类名::new ,并且方法调用传入的参数就是构造方法的参数。

代码如下:

 public class Demo {
public static void main(String[] args) {
// 使用匿名内部类
// NewInterface ni = new NewInterface() {
// @Override
// public String test(String str) {
// return new String(str);
// }
// };
// 使用Lambda表达式
// NewInterface ni = str -> new String(str);
// 使用方法引用
NewInterface ni = String::new;
System.out.println(ni.test("str"));
}
} @FunctionalInterface
interface NewInterface {
String test(String str);
}

运行结果如下:

 str

Java1.8新特性——接口改动和Lambda表达式的更多相关文章

  1. Java8新特性学习笔记(一) Lambda表达式

    没有用Lambda表达式的写法: Comparator<Transaction> byYear = new Comparator<Transaction>() { @Overr ...

  2. Java8新特性(一)——Lambda表达式与函数式接口

    一.Java8新特性概述 1.Lambda 表达式 2. 函数式接口 3. 方法引用与构造器引用 4. Stream API 5. 接口中的默认方法与静态方法 6. 新时间日期 API 7. 其他新特 ...

  3. Java8新特性第1章(Lambda表达式)

    在介绍Lambda表达式之前,我们先来看只有单个方法的Interface(通常我们称之为回调接口): public interface OnClickListener { void onClick(V ...

  4. jdk8新特性-亮瞎眼的lambda表达式

    jdk8之前,尤其是在写GUI程序的事件监听的时候,各种的匿名内部类,大把大把拖沓的代码,程序毫无美感可言!既然Java中一切皆为对象,那么,就类似于某些动态语言一样,函数也可以当成是对象啊!代码块也 ...

  5. Java8新特性 利用流和Lambda表达式对List集合进行处理

    Lambda表达式处理List 最近在做项目的过程中经常会接触到 lambda 表达式,随后发现它基本上可以替代所有 for 循环,包括增强for循环.也就是我认为,绝大部分的for循环都可以用 la ...

  6. java1.8新特性之stream流式算法

    在Java1.8之前还没有stream流式算法的时候,我们要是在一个放有多个User对象的list集合中,将每个User对象的主键ID取出,组合成一个新的集合,首先想到的肯定是遍历,如下: List& ...

  7. java1.8新特性之stream

    什么是Stream? Stream字面意思是流,在java中是指一个来自数据源的元素队列并支持聚合操作,存在于java.util包中,又或者说是能应用在一组元素上一次执行的操作序列.(stream是一 ...

  8. Stream:java1.8新特性

    原 Stream:java1.8新特性 2017年08月01日 18:15:43 kekeair-zhang 阅读数:392 标签: streamjava1-8新特性 更多 个人分类: 日记 版权声明 ...

  9. [二] java8 函数式接口详解 函数接口详解 lambda表达式 匿名函数 方法引用使用含义 函数式接口实例 如何定义函数式接口

    函数式接口详细定义 package java.lang; import java.lang.annotation.*; /** * An informative annotation type use ...

随机推荐

  1. 牛客练习赛22 C 简单瞎搞题

    //位运算 // & 都是1 才是 1 // | 都是0 才是0 // ^ 不一样才是1 #include <iostream> #include <cstdio> # ...

  2. ACM 深度优化搜索算法小总结

    深度优化搜索算法的本质:就是从一状态不断转移,如果无法转移了就需要返回上一个状态,知道找到解为止. 其核心:递归函数 基本模型: dfs(int i, int j) { //控制结束条件 //进行状态 ...

  3. golang连接orcale

    使用glang有一段时间了,最开始其实并不太喜欢他的语法,但是后来熟悉之后发现用起来还挺爽的.之前数据库一直使用mysql,连接起来没有什么问题,github上有很多完善的驱动,所以以为连接其他数据库 ...

  4. JSON Undefined 问题

    在IE6和IE7浏览器下或在IE8-IE10浏览器文档模式为IE7及以下时,控制台会报错:JSON is undefined. 这种错误在IE6和IE7浏览器下出现很正常,因为JSON在IE8+浏览器 ...

  5. oracle 控制文件的重建

    目录 oracle 控制文件的重建 NORESETLOGS RESETLOGS oracle 控制文件的重建 不到最后时刻,如三个控制文件都已损坏,又没有控制文件的备份.还是不要重建控制文件,处理不好 ...

  6. Xampp 配置出现403无法访问

    找到\xampp\apache\conf\httpd.conf配置文件 Access forbidden! You don’t have permission to access the reques ...

  7. mysql primary partition分区

    尝试把数据库一个表分区 ALTER TABLE user PARTITION BY RANGE(TO_DAYS(`date`)) ( PARTITION p1004 VALUES LESS THAN  ...

  8. AtCoder Grand Contest 020

    A - Move and Win Time limit : 1sec / Memory limit : 512MB Score : 300 points Problem Statement A gam ...

  9. C++之Effective STL学习笔记Item14

    使用reserve来避免不必要的重新分配! The reserve member function allows you to minimize the number ofreallocations ...

  10. kb-07线段树--11--区间多重该值多种查询

    /* lazy思想的运用,因为查询多种,如果全记录就太繁了,lazy就是如果该区间的每一个叶子的状态都相同就不用深入下去该值,只要暂时标记下,查询的时候也不用下去,直接计算: */ #include& ...