一.接口内允许添加默认实现的方法
在原来的定义中接口中只能有方法声明,不能有方法体。在Java8中,接口也可以有自己带有实现的方法啦。具体来说是要用default来修饰的方法,其可以像类中的方法一样有执行语句。在实现接口时,可以不实现其default方法,并且实现类对象可以调用其接口的default方法。当然也可以在实现类中覆盖default方法。
public class InterfaceDemo {
public static void main(String[] args) {
Formula formula = new Formula() {
@Override
public double calculate(int a) {
return a + 1;
}
// @Override
// public double sqrt(int a) {
// return a;
// }
};
System.out.println(formula.calculate(100));
System.out.println(formula.sqrt(100));
}
}
interface Formula {
//计算
double calculate(int a);
//求平方根
default double sqrt(int a) {
return Math.sqrt(a);
}
}
二.Lambda表达式
Lambda简化了匿名内部类的写法。Java8中可以通过类型推断来判断出用户的意图,不必将类型等信息写全。特别是方法实现体中只有一句语句的实现类,更能加大简化力度。
Lambda解决了将一个方法作为参数传值的问题。解决了一个函数是否可以独立存在的问题。是Java向函数式编程的一种靠拢。
一般在某个方法只使用一次的地方使用Lambda表达式;如果方法没有入参,则只写一个()->{语句};当只有一个参数,且类型可推断时,()可省略;如果方法体中只有一条语句花括号可以省略;
public class LambdaDemo {
public static void func1() {
System.out.println("not use Lambda");
List strs = Arrays.asList("hello","word","apple","people","sea","book","school","computer");
//old way
Collections.sort(strs,new Comparator() {
@Override
public int compare(String a,String b) {
return b.compareTo(a);
}
});
System.out.println(strs);
}
public static void func2() {
System.out.println("use Lambda");
List strs = Arrays.asList("hello","word","apple","people","sea","book","school","computer");
//way 1
Collections.sort(strs,(String a,String b) -> {
return a.compareTo(b);
});
//way 2 : only one statement
Collections.sort(strs,(String a,String b) -> a.compareTo(b));
//way 3 : omit class
Collections.sort(strs,(a,b) -> a.compareTo(b));
System.out.println(strs);
}
}
三.函数式接口Functional Interface
学习了上述Lambda的内容肯定会有一些疑问:如果接口有多个需要实现的方法 呢,还能使用Lambda?如果可以的话Lambda是如何做推断的。
答案是,使用Lambda时要求接口中只能有一个抽象方法(通过default修饰的带有方法体的接口中的方法不是抽象方法)。
如果一个接口被注解@FunctionalInterface修饰,则该接口只能有一个抽象方法,否则会报错。
四.引用类的构造器及方法
在Lambda中若是直接调用了一个方法,且调用方法的形参和要实现的接口抽象方法形参一致,则可以进一步简写。举例如下:
接口定义:
@FunctionalInterface
interface Converter {
T convert(F form);
}
Lambda表达式
//旧的写法
Converter convert1 = (from) -> Integer.valueOf(from);
//新的写法
Converter convert2 = Integer::valueOf;
System.out.println(convert1.convert("1234"));
引用其他类型的方法
- 例子中的方法是Integer类的静态方法,如果是某个类的实例方法,则应该使用一个对象加::来引用。如,obj::func;
- 如果要调用的一个构造方法(抽象方法返回的是一个对象),则应该这样使用:Integer::new。用new代替构造方法名字。
五.Lambda访问外部变量及接口默认方法
访问局部变量
- 可以访问局部的final变量,但不能修改。
- 与匿名内部类不同的是,外部变量不需要显示地声明为final,但却要有final的特点,不能被修改,在Lambda之后被修改也不行。
访问成员变量和静态变量
可以任意读写,举例如下:
public class FunctionInterface {
String str = "init";
static Integer num = 0;
public void testScope() {
Converter convert = (from) -> {
//在这里可访问成员变量和静态变量
str = from;
num = Integer.valueOf(from);
return num;
};
System.out.println(convert.convert("3453"));
System.out.println(convert.convert("5678"));
}
}
访问接口的默认方法
在匿名类中可以访问接口定义的默认方法,在Lambda中不可以访问。
六.内置函数式接口
Java8中内置了许多函数式接口,包括Comparator和Runnable等,它们被添加了@FunctionalInterface注解,以用来支持Lambda表达式。
6.1Predicate断言
查看源码,这个函数式接口中要实现的方法为:boolean test(T t); 即一个判断传入值真假的方法,当然判断的规则由你自己定义。
如定义一个判断字符串长度是否大于10的Predicate:
import java.util.function.Predicate;
public class PredicateDemo {
public static void main(String[] args) {
Predicate predicate = s -> s.length() > 10;
System.out.println(predicate.test("hello"));
System.out.println(predicate.test("hello,world!"));
}
}
6.2 Function
查其源码,需要实现一个R apply(T t)的方法。这个接口提供链式调用、组合的功能。
6.3 Supplier
Supplier
personSupplier.get();
6.4 Consumer
??
Consumer
greeter.accept(new Person("Luke", "Skywalker"));
6.5 Optional
参考资料
七.Stream流
什么是Stream流?
参考资料
Stream流提供了一种对集合Collection的方便的操作。分为“中间操作”和“终端操作”两种。中间操作的结果还是返回一个Stream,可以继续操作;而终端操作会返回一个结构不能继续流操作了。
要使用Sream首先要通过Collection的Stream方法获取一个Stream对象。
7.1 Filter过滤 中间操作
筛选出集合中满足一定条件的元素。Stream有一个filter方法,入参是一个Predicate,筛选结果是Predicate.test为true的集合的Stream。下面来看一个筛选出String集合中以"s"开头的String的程序:
List list = Arrays.asList("hello","world","apple","people","sea",
"watch","table","book","school","help");
list
.stream() //获取stream
.filter(s -> s.startsWith("s")) //设置filter。中间操作
.forEach(System.out::println); //打印出流中的元素。终端操作
7.2 Sorted排序 中间操作
可以给sorted()方法传入一个Comparator用来自定义排序,否则将使用默认排序规则。
list
.stream()
.sorted((a,b) -> b.compareTo(a))
.forEach(System.out::println);
7.3 Map
map方法入参为一个Function函数式接口。调用map方法将对集合中的每一个元素执行一下Function中的apply方法,并返回由其返回值组成的集合的流。
举例:将表示数字的字符串集合全部转换为数字再加一后输出。
List list = Arrays.asList("10","100","1000");
System.out.println("---- test map ----");
list
.stream()
.map(Integer::valueOf) //转换为整数
.map(a -> a + 1) //执行加一操作
.forEach(System.out::println);
7.4Match匹配
是种终端操作,结果不是stream对象,而是boolean值
根据Predicate指定的规则判断集合中是否有匹配的,有的话返回true。
有三种形式,anyMatch:有一个匹配就返回true。allMatch:全部匹配返回true。noneMatch:全部不匹配返回true。
boolean anyMatch(Predicate predicate);
boolean allMatch(Predicate predicate);
boolean noneMatch(Predicate predicate);
7.5 Count计数
终端操作,统计stream中元素的个数。
long count =
list
.stream() //获取stream
.filter(s -> s.startsWith("s")) //设置filter。中间操作
.count();
7.6 Reduce
list[0]和list[1]执行操作,得到的结果为result。result再和list[2]执行操作,得到的结果result。依次进行,对所有元素执行一遍。
根据上述描述也可以看出,这里的“操作”必须满足两个入参、返回值是同一类型的。
reduce方法的入参是:BinaryOperator
举例:求Integer集合中所有元素的和。
List list = Arrays.asList(1,2,3,4,5);
System.out.println("---- test reduce ----");
Optional sum =
list
.stream()
.reduce((a,b) -> a + b);
sum.ifPresent(System.out::println);
注意:reduce的返回值为Optional