一、接口定义增强(新增默认方法和静态方法)
接口(Interface)在开发中广泛应用,如果接口设计不合理,那么接口的所有实现类都会受到影响。因此JDK1.8对接口做了优化,新增了默认方法和静态方法。
- 默认方法:使用default定义,通过对象调用
JDK1.8之前接口中的方法只有声明而没有方法体,具体方法在子类中实现。但如果某个方法是通用的,在所有实现类中的实现都是一样的,我们还有必要每个子类中都写一遍吗?当然没有必要!因此JDK1.8提供了默认方法,对于接口中的这种通用方法,可以在接口中直接写出方法体,子类无需实现就可通过对象调用。另外默认方法是支持重写的,子类可以根据具体需求更改默认方法。
interface IFunction{
default void print(){
System.out.println("我是通用的打印方法");
}
}
- 静态方法:使用static定义,通过接口名调用
interface IFunction{
void print();
//静态方法
static IFunction getInstance(){
return new FunctionImpl();
}
}
class FunctionImpl implements IFunction{
@Override
public void print() {
System.out.println("test");
}
}
public class Demo{
public static void main(String[] args) {
//通过接口名调用
IFunction iFunction = IFunction.getInstance();
iFunction.print();
}
}
有没有发现此时的接口和抽象类非常相似?接口和抽象类到底有何异同(以JDK1.8为准):
相同点:
- 二者都是抽象类型;
- 都可以有具体的实现方法;
- 所有方法的实现不是必须的。
不同点:
- 抽象类不可以多继承,而接口可以;
- 设计理念不同。抽象类表示的是"is-a"关系,接口表示的是"like-a"关系;
- 接口中定义的变量默认是public static final,必须初始化且之后不能改变;而抽象类中的变量是default,可以在子类中重新定义和赋值。
二、lambda表达式
使用lambda表达式,可以简化代码,这是函数式编程的优点,而面向对象编程结构必须完整,下面通过对比来直观感受一下。
面向对象编程:
interface IFunction{
void print();
}
public class Demo implements IFunction{
@Override //实现接口方法
public void print() {
System.out.println("test");
}
public static void main(String[] args) {
IFunction Fc = new Demo();
Fc.print();
}
}
函数式编程:
@FunctionalInterface
interface IFunction{
void print();
}
public class Demo{
public static void main(String[] args) {
//使用lambda表达式,一行代码完成
IFunction Fc = () -> System.out.println("test");
Fc.print();
}
}
当然这只是举个简单例子,但是可以看到函数式编程明显代码简洁很多。那么lambda表达式是什么,怎么用呢?
- lambda表达式的底层实现是匿名内部类。
- lambda表达式中的操作符 “ -> ” 将表达式分为了两部分,左侧 “()” 里面是参数,右侧是功能实现。
Lambda表达式的语法:
(参数)-> {语句};
下面具体来看几种情况:
1、无参,无返回值
示例:Test t = () -> System.out.println("test");
2、一个参数
示例:Test t = (x) -> System.out.println(x);
(小括号也可以省略写成:Test t = x -> System.out.println(x); )
3、两个参数,并且有返回值
Test t = (x,y) -> {
System.out.println("test");
return x+y;
};
//当只有一条语句时,return和{}可以省略不写
Test t = (x,y) -> x+y;
4、类型推断:上面的示例中,都省略了Lambda 表达式中参数列表的数据类型。因为JVM编译器可通过上下文推断出数据类型。
三、方法引用
所谓方法引用,就是如果lambda表达式中的方法有现成的可以使用,那么就直接引用。因此方法引用可以看做是lambda表达式的另一种写法,其语法结构更加简单。方法引用的类型有四种:
- 引用静态方法:类名称 :: static 方法名称 ;
我们来看下图的例子,Idea提示将lambda表达式替换为方法引用。
替换后结构更加简单:
- 引用某个对象的方法:实例化对象 :: 普通方法 ;
@FunctionalInterface
interface IUtil<R>{
R switchPara();
}
public class Test {
public static void main(String[] args) {
IUtil<String> util = "hello"::toUpperCase;
String str = util.switchPara(); //转换的就是"hello"
System.out.println(str);
}
}
- 引用某个特定类的方法:类名称 :: 普通方法 ;
@FunctionalInterface
interface IUtil<R,T>{
R compare(T t1,T t2);
}
public class Test {
public static void main(String[] args) {
//compareTo()为String类中的普通方法
IUtil<Integer,String> util = String::compareTo;
int r = util.compare("111","222");
System.out.println(r);
}
}
- 引用构造方法:类名称 :: new ;
class Student{
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
@FunctionalInterface
interface IUtil<R,PN,PA>{
R createStudent(PN p1,PA p2);
}
public class Test {
public static void main(String[] args) {
IUtil<Student,String,Integer> util = Student::new; //引用构造方法
Student stu = util.createStudent("tony",18);
System.out.println(stu);
}
}
四、内建函数式接口
所谓函数式接口:就是接口中只有一个抽象方法,并且加上注解@FunctionalInterface。函数式接口是lambda表达式的核心。
函数式接口分以下四种(都是java自带的):
- 功能型函数式接口:有参有返回
public interface Function<T, R> {
R apply(T t);
}
- 供给型函数式接口:无参有返回
public interface Supplier<T> {
T get();
}
- 消费型函数式接口:有参无返回
public interface Consumer<T> {
void accept(T t);
}
- 断言型函数式接口:有参有返回,返回lboolean类型
public interface Predicate<T> {
boolean test(T t);
}
来看具体例子:
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.Consumer;
import java.util.function.Predicate;
public class Demo{
public static void main(String[] args) {
Function<String,Integer> func = Integer::parseInt;
//功能型:
Integer num =func.apply("123456");
System.out.println(num);
//供给型
Supplier<String> supplier = "hello"::toUpperCase;
String str = supplier.get();
System.out.println(str);
//消费型
Consumer<String> consumer = System.out::println;
consumer.accept("world");
//断言型
Predicate<String> predicate = "**123**"::contains;
boolean effect = predicate.test("123");
System.out.println(effect);
}
}