Java8特性
接口定义增强
JDK1.8之前,接口的组成只有全局变量
和抽象方法
。从JDK1.8开始,接口的组成增加了。
假设:现有一个接口,其子类有2W个。现在发现该接口功能不足,要增加一个方法,该方法对于所有子类而言的功能是一样的(即方法体是一样的)。此时要修改每个子类的方法,要修改2W次。
上述问题在JDK1.8中不存在,因为其允许接口中定义普通方法,但普通方法必须使用default定义。
interface Fruit {
public void print(); // 接口原本定义的方法
default public void fun() { // 普通方法
System.out.println("JDK1.8");
}
}
class Apple implements Fruit {
@Override
public void print() {
System.out.println("苹果");
}
}
public class Demo {
public static void main(String[] args) {
Fruit f = new Apple();
f.fun();
f.print();
}
}
除了使用default
定义方法,还可以使用stati
c定义方法
范例:定义static方法
interface Fruit {
public void print(); // 接口原本定义的方法
default public void fun() { // 普通方法
System.out.println("JDK1.8");
}
static void get() {
System.out.println("直接由接口调用");
}
}
class Apple implements Fruit {
@Override
public void print() {
System.out.println("苹果");
}
}
public class Demo {
public static void main(String[] args) {
Fruit f = new Apple();
f.fun();
f.print();
Fruit.get();
}
}
JDK1.8有个新功能:内部类访问方法参数时可以不加上final关键字。
这些新特性,完全打破了Java已有的代码组成形式。
Lamda表达式
Lamda属于函数式编程的概念,下面通过匿名内部类,来分析函数式编程的产生目的。
范例:匿名内部类
interface Fruit {
public void print(); // 接口原本定义的方法
}
public class Demo {
public static void main(String[] args) {
fun(new Fruit() {
@Override
public void print() {
System.out.println("水果");
}
});
}
public static void fun(Fruit fru) {
fru.print();
}
}
上述代码中fun()最终需要的只是输出,但是由于Java的开发结构的完整性要求,不得不在这个核心语句上嵌套更多的内容。但是该做法过于严谨复杂,在JDK1.8引入函数式编程,简化过程。
范例:使用Lamda表达式
interface Fruit {
public void print(); // 接口原本定义的方法
}
public class Demo {
public static void main(String[] args) {
fun(()-> System.out.println("水果"));
}
public static void fun(Fruit fru) {
fru.print();
}
}
Lamda语法有三种形式:
·(参数)->单行语句;
·(参数)->{单行语句};
·(参数)->表达式;
范例:观察有参单行
interface Fruit {
public void print(String str); // 接口原本定义的方法
}
public class Demo {
public static void main(String[] args) {
// 首先要定义此表达式里面需要接收变量,单行语句直接进行输出
fun((s) -> System.out.println(s));
}
public static void fun(Fruit fru) {
fru.print("苹果"); // 设置参数的内容
}
}
范例:编写多行语句
interface Fruit {
public void print(String str); // 接口原本定义的方法
}
public class Demo {
public static void main(String[] args) {
// 首先要定义此表达式里面需要接收变量,单行语句直接进行输出
fun((s) -> {
s = s.toUpperCase();
System.out.println(s);
});
}
public static void fun(Fruit fru) {
fru.print("Hello"); // 设置参数的内容
}
}
范例:编写表达式
interface Fruit {
public int add(int x, int y);
}
public class Demo {
public static void main(String[] args) {
// 首先要定义此表达式里面需要接收变量,单行语句直接进行输出
fun((s1, s2) -> s1 + s2);
}
public static void fun(Fruit fru) {
System.out.println(fru.add(10, 20));
}
}
方法引用
对象引用的特点:不同对象可以操作同一块内容。而方法引用就是指为一个方法设置别名,相当于一个方法定义了不同的名字。
1、方法引用在Java8中一共定义了四种形式:
· 引用静态方法: 类名称::static 方法名称;
· 引用某个对象的方法:实例化对象::普通方法;
· 引用特定类型的方法:特定类::普通方法;
· 引用构造方法:类名称::new。
范例:引用静态方法
String类中的valueof():public static String valueof(int x)
package com.java.demo;
/**
* 只有一个方法的接口
* @param <P> 参数的数据类型
* @param <R> 返回值的数据类型
*/
interface Math<P, R> {
public R exchange(P p);
}
public class Demo {
public static void main(String[] args) {
// 覆写了exchange(),使其具有valueOf()的功能
Math<Integer, String> math = String::valueOf;
String msg = math.exchange(10000);
// 将所有的0替换成9
System.out.println(msg.replaceAll("0", "9"));
}
}
范例:普通方法引用
String类中的toUpperCase():public String toUpperCase()
package com.java.demo;
interface Math<R> {
public R upper();
}
public class Demo {
public static void main(String[] args) {
// 覆写了upper,使其具有toUpperCase的功能
// toUpperCase是普通方法,必须由String对象调用
// hello是String对象,代码如下
Math<String> math = "hello"::toUpperCase;
String msg = math.upper();
System.out.println(msg);
}
}
上述例子显示,要实现方法引用,必须要有接口,且该接口只能有一个方法。为了保证该接口只有一个方法,可对其进行注解说明。
@FunctionalInterface // 此为函数式接口,只能定义一个方法
interface ITest<R> {
public R upper();
}
2、在进行方法引用的过程中,还有一种形式的引用(这种形式需要特定类的对象支持)。一般使用“类::方法”,引用的是类中的静态方法。但是这种形式也可以引用普通方法。
例如:在String类中有一个方法:public int compareTo(String anotherString)
,比较的形式是String对象1.compareTo(String对象2)
,即要引用该方法,需要有两个参数。
范例:引用特定类的方法
interface IMessage<P> {
public int compare(P p1, P p2);
}
public class Demo {
public static void main(String[] args) {
IMessage<String> msg = String::compareTo;
System.out.println(msg.compare("A", "B"));
}
}
与之前相比,方法引用前不需要定义对象,可以理解为将对象定义在参数上。
范例:引用构造方法
interface IMessage<C> {
public C create(String t, double p);
}
class Book {
private String title;
private double price;
public Book(String title, double price) {
this.title = title;
this.price = price;
}
public String toString() {
return "书名:" + this.title + ",价格:" + this.price;
}
}
public class Demo {
public static void main(String[] args) {
IMessage<Book> msg = Book::new; // 引用构造方法
// 虽然调用的是create(),实际引用了Book类的构造方法
Book book = msg.create("Java开发", 66.6);
System.out.println(book);
}
}
对象引用是使用不同的名字,而方法引用需要一个函数接口。
内建函数式接口
1、JDK1.8中提供了一个包:java.util.function
,提供有以下四个核心接口:
(1)功能性接口(Function):public interface Function<T,R>{public R apply(T t);}
|- 此接口需要接收一个参数,并且返回一个处理结果;
(2)消费型接口(Consumer):public interface Consumer{public void accept(T t);}
|- 此接口只负责接收数据(引用数据是不需要返回的),并且不返回结果;
(3)供给型接口(Suplier):public interface Supplier{public T get();}
|- 此接口不接收参数,但是可以返回结果
(4)断言型接口(Predicate):public interface Predicate{public boolean Test(T t);}
|- 进行判断操作使用;
在JDK1.8中存在以上四个功能型接口,所以很少会由用户去定义新的函数式接口。
范例:函数式接口——接收参数并返回处理结果
· String类有一个方法:public boolean startsWith(String str)
package com.java.demo;
import java.util.function.Function;
public class Demo {
public static void main(String[] args) {
Function<String, Boolean> fun = "##hello"::startsWith;
System.out.println(fun.apply("##")); // true
}
}
范例:消费型接口
package com.java.demo;
import java.util.function.Consumer;
class MyDemo {
// 此方法没有返回值,但是有参数
public void print(String str) {
System.out.println(str);
}
}
public class Demo {
public static void main(String[] args) {
Consumer<String> cons = new MyDemo()::print;
cons.accept("Hello World");
}
}
范例:供给型接口
· 引用String类的toUpperCase():public String toUpperCase()
;
package com.java.demo;
import java.util.function.Supplier;
public class Demo {
public static void main(String[] args) {
Supplier<String> sup = "hello"::toUpperCase;
System.out.println(sup.get());
}
}
范例:断言型接口
· String类中有equalsIgnoreCase()
import java.util.function.Predicate;
public class Demo {
public static void main(String[] args) {
Predicate<String> pre = "hello"::equalsIgnoreCase;
System.out.println(pre.test("Hello"));
}
}