文章内容简要:
1、java8中接口方法的变化
2、Lambda表达式
3、函数式接口
4、方法引用
1、java8中接口方法的变化
-接口中方法默认使用public abstract修饰,字段默认使用public static final修饰;
-java8中可以定义default修饰的方法,也可以定义static修饰的方法;
-default修饰的方法,实现类可以重写,也可以不重写;
-static修饰的方法,实现类获取不到也无法重写(属于接口,直接通过接口.方法调用);
-default修饰的方法应用场景:例如,某个接口已经上线而且实现类很多,因为某个需求需要增加个通用功能,可以在接口里定义一个default方法,而不去一一修改实现类。接口中的新方法使用default修饰,是对接口的一个增强,而且可以不用去修改其实现类;
-static修饰的方法应用场景:该方法可以直接调用,实现类不能去修改,同样是对接口的一个升级增强。
2、Lambda表达式
可以当成是一个匿名方法,可以使代码更简洁
语法:
(参数列表) -> {语句;}
Lambda表达式由参数列表和一个Lambda体组成,通过“->”连接。
说明:
1)当只有一个参数时,参数列表的“()”是可以省略的;
x -> {System.out.println(x);}
2)参数列表中参数的数据类型可以省略;
(x,y) -> {x.compareTo(y);}
3)Lambda体只有一条语句时,如果是return语句,“{}”和return只能同时省略,不可省略其中一个;如果是非return语句,“{}”可以省略
x -> “111”; 等价于 x -> {return “111”;};
x -> System.out.println(“111”); 等价于 x -> {System.out.println(“111”);};
4)没有参数也可以是一个Lambda表达式。
() -> “hello world”; (返回一个字符串)
以下是符合要求的Lambda表达式
public class Demo1 {
public static void main(String[] args) {
Runnable runnable = () -> {};
Callable<String> callable = () -> "hello world";
IntBinaryOperator intBinaryOperator = (a, b) -> a + b;
IntBinaryOperator intBinaryOperator2 = (int a, int b) -> {return (a + b);};
IntFunction<Integer> integerIntFunction = a -> { return a+2; };
Function function = a -> {return "111";};
Function function2 = a -> "111";
Consumer<String> consumer = a -> { System.out.println("111"); };
Consumer<List<String>> listConsumer = (List<String> list) -> {list.isEmpty();};
Callable<Object> callable1 = () -> {return new Object();};
Function<Object, Integer> function1 = (Object obj) -> obj.hashCode();
Consumer<Object> consumer1 = obj -> System.out.println(111);
}
}
3、函数式接口
函数式接口:只定义一个抽象方法的接口。例如:Comparator Runnable
函数式接口使用注解@FunctionalInterface声明。
函数式接口是为Lambda表达式准备的,或者说Lambda表达式必须实现一个函数式接口。
/**
* @FunctionalInterface 声明接口为函数式接口
*/
@FunctionalInterface
public interface AddFunctionInterface {
Integer add(Integer a,Integer b);
}
/**
* 只继承了一个抽象方法,是函数式接口
*/
public interface ByteAddInterface extends AddFunctionInterface {
}
/**
* 加上继承的方法,共有两个抽象方法,不是函数式接口
*/
public interface DoubleAddInterface extends AddFunctionInterface {
Double add(Double a, Double b);
}
3.1 Predicate<T>
接口
它的抽象方法表示判断是否符合条件,符合返回true,不符合返回false;
`boolean test(T t);`
定义一个方法,将原集合进行数据过滤,使用predicate返回需要的集合。此思路可在项目中作为工具来使用,当一个方法需要对数据做不确定的变化时,可以接收一个predicate参数,在调用时自行定义不同的规则。
public class Demo1 {
public static void main(String[] args) {
/* Predicate */
List<Integer> list = Arrays.asList(11,22,33,44,55);
System.out.println("原list:" + list);
Predicate<Integer> predicate = t -> t >25 ;
List newList = getNewList(list,predicate);
System.out.println("新list:" + newList);
Predicate predicate = t -> t instanceof String;
boolean flag = predicate.test(2);
System.out.println(flag); //false
flag = predicate.test("abc");
System.out.println(flag); //true
}
/**
* 将一个list中符合条件的数据放到新的list返回
* @param predicate 指定一个条件(规则)
*/
public static <T> List<T> getNewList(List<T> list, Predicate<T> predicate){
List<T> newList = new ArrayList<>();
list.forEach(t -> {
if (predicate.test(t)){ //符合条件返回true
newList.add(t);
}
});
return newList;
}
}
3.2 Consumer<T>
接口
抽象方法,需要访问一个对象并进行操作,不需要返回值时可以使用这个接口。
void accept(T t);
集合的forEach方法就是使用的Consumer接口
public class Demo1 {
public static void main(String[] args) {
/* Consumer */
List<String> list = Arrays.asList("a","b","d","x","c");
list.forEach(t -> System.out.println(t));
Map<String,String> map = new HashMap<>();
map.put("a","12");
map.put("b","22");
map.put("c","32");
map.forEach((k,v) -> System.out.println("key:"+k+",value:"+v));
map.forEach((k,v) -> {
System.out.print("key:"+k);
System.out.println(",value:"+v);
});//两个输出结果一样
}
}
3.3 Function<T,R>
接口
抽象方法,需要输入一个对象,经过处理后返回新的值,可以使用这个接口
R apply(T t);
定义一个对原数据处理后返回新结果的函数,同样可以在项目中使用:接收数据,返回其它数据,具体实现方法不同,可以通过函数式接口作为参数来传入不同的逻辑
public class Demo1 {
public static void main(String[] args) {
/* Function */
List<Integer> list = Arrays.asList(11,22,33,44);
Function<Integer,String> function = t -> t * new Random().nextInt(10)+"-";
List<String> newList = getNewList(list, function);
System.out.println(newList);
list.forEach(t -> newList.add(function.apply(t)));
System.out.println(newList);
List<String> stringList = Arrays.asList("123","a","32","aaaaa");
Function<String,Integer> fun = t -> t.length(); //返回每个数据的长度
stringList.forEach(t -> System.out.println(t + "的长度:"+fun.apply(t)));
}
/**
* 将一个集合的数据经过处理后写入到另一个集合
* @param function 处理数据的方法
*/
public static <T,R> List<R> getNewList(List<T> list,Function<T,R> function){
List<R> res = new ArrayList<>();
list.forEach(obj -> res.add(function.apply(obj))); //把通过apply方法处理后的数据保存到集合中
return res;
}
}
3.4 Supplier<T>
接口
抽象方法,不需要接收参数,直接返回一个对象
T get();
直接返回一个固定值,无须输入参数
public class Demo1 {
public static void main(String[] args) {
/* Supplier */
Supplier supplier = () -> 1;
System.out.println(supplier.get());
}
}
3.5 对基本数据类型的处理
函数式接口的泛型只能接收引用类型,不能直接使用基本类型。
JDK8中为基本类型提供了对应的函数式接口,避免在进行基本类型的输入输出时频繁进行装箱-拆箱操作(耗内存,耗时)。
针对基本类型数据的函数式接口名称前面加了基本类型名的前缀,例如:IntPredicate,IntConsumer,IntFunction等
public class Demo1 {
public static void main(String[] args) {
/* 基本数据类型 */
IntPredicate intPredicate = (i -> i*2 == 2);
System.out.println(intPredicate.test(1));
System.out.println(intPredicate.test(2));
IntConsumer intConsumer = (i -> System.out.println(i));
intConsumer.accept(1);
intConsumer.accept(2);
IntFunction intFunction = (i -> i*2);
System.out.println(intFunction.apply(1));
System.out.println(intFunction.apply(2));
}
}
总结:
1、Predicate接口,接收参数,返回boolean,进行判断,方法是test(),数据进行逻辑判断使用;
2、Consumer接口,接收参数,没有返回值,只执行语句,方法是accept(),消费数据,无返回;
3、Function接口,接收参数,可以返回其它数据类型,可当做是一个正常的方法,方法是apply(),可根据一个类型数据转成另一类型数据;
4、Supplier接口,不接收参数,可以返回其它数据类型,返回固定值,方法是get(),提供数据,无须入参;
5、对于基本数据类型,有对应名称作为前缀的函数式接口可直接使用。
6、函数式接口可以使用变量,但是这个变量必须是final修饰的或者不再改变的才可以(即使是执行后才对变量进行了赋值也是不可以的)
4、方法引用
可以重复使用现有的方法定义,像Lambda一样传递,如:
list.forEach(x -> System.out.println(x));
使用方法引用的话可以改成如下:
list.forEach(System.out::println);
方法引用可以看作是仅仅调用特定方法的Lambda表达式的一种快捷写法,需要使用方法引用时,目标引用(类名)放在"::“前,方法名放在”::“后面,只需要方法名,不需要”()",例如:
(String s) -> s.length(); 等同于 String::length;
public class Demo1 {
public static void main(String[] args) {
List list = Arrays.asList(3,5,6,1);
list.forEach(t -> System.out.println(t));
list.forEach(System.out::println); //二者等效
String str = "123456";
Function<String, Integer> fun = (s) -> s.length();
Function<String, Integer> fun1 = String::length;
Assert.isTrue(fun.apply(str)==fun1.apply(str),()->"不相等");
}
}
方法引用主要分为四类:
1、指向静态方法
public class Demo1 {
public static void main(String[] args) {
Integer i = 123;
Function function = String::valueOf;
System.out.println(function.apply(i) instanceof String);
Integer[] arr = {516,3,81,20,66};
Arrays.sort(arr,Integer::compareTo); //对数组排序
System.out.println(Arrays.toString(arr));
}
}
2、指向实例方法
public class Demo1 {
public static void main(String[] args) {
BiFunction<Demo1, Demo1, Boolean> compare = Demo1::compare;
System.out.println(compare.apply(new Demo1(),new Demo1()));
}
public boolean compare(Demo1 b){
return this.equals(b);
}
}
3、指向现有对象的实例方法引用
public class Demo1 {
public static void main(String[] args) {
String s = "abc";
System.out.println(s.compareTo("123"));
Function<String, Integer> function1 = s::compareTo;
System.out.println(function1.apply("123"));
}
}
4、指向构造方法的引用
对于一个现有的构造方法,可以使用类名和new来创建对象,需要注意,三个及以上参数的构造方法需要自定义函数式接口去实现
public class Demo1 {
public static void main(String[] args) {
Supplier<Demo1> sup = Demo1::new; //无参构造
System.out.println(sup.get());
Function<String, Demo1> fun = Demo1::new; //一个参数构造
System.out.println(fun.apply("aaa"));
BiFunction<String, String, Demo1> bi = Demo1::new; //两个参数构造
System.out.println(bi.apply("aaa","bbb"));
ConstructionFunction<String, String, Integer, Demo1> function = Demo1::new;//三个参数及以上构造需要自己定义接口
System.out.println(function.apply("aaa","bbb",123));
}
//field,setter,getter,construction,toString省略
}
/**
* 自定义多个构造参数接口
*/
@FunctionalInterface
interface ConstructionFunction<T,U,V,R>{
R apply(T t,U u,V v);
}
Lambda表达式小练习
public class Test {
public static void main(String[] args) {
List<Student> list = Arrays.asList(new Student("zs",22,"北京市")
,new Student("ls",33,"上海市")
,new Student("ab",32,"重庆市")
,new Student("mm",25,"天津市"));
//原始数据
System.out.println("old data:"+list);
//匿名内部类
list.sort(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getAge()-o2.getAge();
}
});
System.out.println("age asc:"+list);
//Lambda表达式
list.sort((s1,s2) -> s2.getAge().compareTo(s1.getAge()));
System.out.println("age desc:"+list);
BiFunction<String, String, Integer> comparator = String::compareTo;
list.sort((s1,s2) -> comparator.apply(s1.getAdd(),s2.getAdd()));
System.out.println("add asc:"+list);
//Comparator接口中comparing静态方法
list.sort(Comparator.comparing(Student::getName));
System.out.println("name asc:"+list);
}
}
class Student{
private String name;
private Integer age;
private String add;
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
public String getAdd() {
return add;
}
public Student() {
}
public Student(String name, Integer age, String add) {
this.name = name;
this.age = age;
this.add = add;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", add='" + add + '\'' +
'}';
}
}
定义一个函数式接口
public class Demo {
public static void main(String[] args) {
MyFunctionInterface myFunctionInterface = new MyFunctionInterface() {
@Override
public void m() {
System.out.println("匿名内部类里实现函数式接口的方法");
}
};
myFunctionInterface.m();
MyFunctionInterface myFunctionInterface1 = () -> System.out.println("Lambda表达式实现函数式接口的方法");
myFunctionInterface1.m();
}
}
@FunctionalInterface
interface MyFunctionInterface {
void m();
}