Java8-Lambda表达式
Java8-Lambda
1. Lambda表达式
1.1 Lambda表达式介绍
1.1.1 lambda表达式作用
- lambda表达式是Java8的一个新特性,当我们在需要使用实现了某些接口的实例时,即便是该实例只在某一处使用,我们也得为它新建一个实现类(最起码也得使用匿名类来创建该实例)
- 自JDK8开始,提供了lambda表达式语法特性,能够极大地简化代码量,在线程创建,集合Stream操作等有着广泛应用
1.2 函数式接口
1.2.1 函数式接口介绍
(1)什么是函数式接口?
- 函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口
- 在JDK8中,接口中提供了
default
方法,使用default
接口修饰的方法在接口定义中有默认的实现,表示该接口的实现类中可以不去实现该方法而使用接口的默认实现; - 要是用lambda表达式,需要接口的某一个方法必须要靠类去实现,被 default 修饰的方法会有默认实现,不是必须被实现的方法,所以不影响 Lambda 表达式的使用
- 在JDK8中,接口中提供了
- 函数式接口可以被隐式转换为 lambda 表达式。
(2)通过注解表示函数式接口
-
可以在任意函数式接口上使用
@FunctionalInterface
注解,这样做可以检测它是否是一个函数式接口,同时 javadoc也会包含一条声明,说明这个接口是一个函数式接口。-
当我们在接口中使用
@FunctionalInterface
,编译器会自动检查该接口是否符合定义,如果不符合函数式接口定义会报错 -
在实际开发者有两个比较常见的函数式接口:
Runnable
接口,Comparator
接口@FunctionalInterface public interface Runnable { ... }
-
1.3 lambda表达式的使用
1.3.1 lambda表达式与传统做法相比
-
在介绍使用lambda表达式之前,我们先来看看原先做法是怎样的,进一步体会到lambda表达式的优点
-
自定义函数式接口
@FunctionalInterface interface GreetingService { void sayMessage(String message); }
(1)创建实现该接口的类
-
传统实现方式:通过创建类来实现接口
class GreetingServiceImpl implements GreetingService{ @Override public void sayMessage(String message) { System.out.println("Hello "+message); } }
-
实例化该接口并使用
public class LamdaTest { public static void main(String[] args) { GreetingService greetingService = new GreetingServiceImpl(); greetingService.sayMessage("Ni187");//Hello Ni187 } }
(2)使用静态内部类
-
通过在使用该接口的类中创建一个静态内部类内部,实现接口方法,进而使用内部类的方法
-
实际上代码量并没有比传统方法少,因为它只是将类的声明位置换到内部而已,但是由于我们创建的接口会只在这里使用,所以使用静态内部类会提高代码的可读性
public class LambdaTest { static class GreetingServiceImpl02 implements GreetingService{ @Override public void sayMessage(String message){ System.out.println("Hello "+message); } } public static void main(String[] args) { GreetingService greetingService = new GreetingServiceImpl(); greetingService.sayMessage("Ni187"); //Hello Ni187 } }
(3)使用局部内部类
-
在通过使用静态内部类时,如果只是仅仅某一个方法中需要接口的实例化对象,那么我们可以通过创建局部内部类来实例化接口,进一步提高代码的可读性
public class LambdaTest { public static void main(String[] args) { class GreetingServiceImpl02 implements GreetingService{ @Override public void sayMessage(String message){ System.out.println("Hello "+message); } } GreetingService greetingService = new GreetingServiceImpl02(); greetingService.sayMessage("Ni187"); //Hello Ni187 } }
(4)使用匿名类
-
什么是匿名类?
-
当我们需要实现某个接口,或者在继承关系中需要重写父类的某个或某些方法时,我们可以在
new 父类名或接口名{内容}
以这种方式来简化类的声明写法,从而提高我们编码的效率 -
匿名类的内部书写规则就如同普通类的写法,通过
new
之后的类名或接口名表示它是某一类的子类或者是接口的实现类,我们可以继承原来类的方法,属性等,也可以重写原来方法,或者添加某些方法,只是这个类没有具体的类名而已-
通过反射获取匿名类Class对象
Class r = new Runnable { @Override public void run() {} public void run2() {} }.getClass(); System.out.println(r);//class com.niss.LambdaTest$1
-
通过反射获取局部类Class对象
Class RunImpl implements Runnable(){ @Override public void run() {} } System.out.println(RunImpl.class);//class com.niss.LambdaTest$1RunImpl
-
-
-
匿名类的使用方法如下:
public class LambdaTest { public static void main(String[] args) { GreetingService greetingService = new GreetingService() { @Override public void sayMessage(String message) { System.out.println("Hello "+message); } }; greetingService.sayMessage("Ni187"); //Hello Ni187 } }
-
可以看出当我们只在某些特定场景使用接口时,完全可以通过创建匿名类的方式来简化编码流程
(2)lambda表达式
-
以下是使用lambda语法进行接口的实现
public class LambdaTest { public static void main(String[] args) { GreetingService greetingService = message->System.out.println("Hello "+message); greetingService.sayMessage("Ni187 ");//Hello Ni187 } }
-
可以看出当我们使用lambda表达式时,代码只保留最核心的部分,注重实现的逻辑,其余统统被精简掉,这就是lambda表达式的一个优势所在
1.4 lambda表达式语法
1.4.1 Lambda表达式基本语法
基本语法:
(parameters) ->{ statements }
;- 当参数只有一个且不声明类型时可以省略
()
- 当后边方法体只有一条语句时可以省略
{}
- 当该语句为返回结果语句时,不可以加
return
关键字,除非不省略{}
- 当该语句为返回结果语句时,不可以加
- 参数类型可以省略,但是必须全部省略或全部保留
- 当引用的类方法或对象方法支持泛型时,可以在引用时加上加上泛型约束
1.4.2 使用示例
(1)常规使用
-
接口定义
interface GreetingService { String sayMessage(String message,int age); }
-
以下表示方法都等价
GreetingService greetingService = (message,age)->"Hello "+message+age;
GreetingService greetingService = (String message,int age)->"Hello "+message+age;
GreetingService greetingService = (String message,int age)->{return "Hello "+message+age;};
(2)常用接口通过lambda表达式进行简化构造
-
通过Lambda表达式实现
Comparator
接口进行排序//定义的person类 class Person{ private int id; private String name; 构造方法,getter,setter @Override public String toString() {...} } public class LambdaTest { public static void main(String[] args) { LinkedList<Person> persons = new LinkedList(); persons.add(new Person(10,"Tom")); persons.add(new Person(20,"Bob")); persons.add(new Person(15,"Com")); persons.add(new Person(20,"Alice")); //通过使用lambda表达式,实例化Comparator接口,完成排序工作 persons.sort((person1, person2)->{ if(person1.getId()!=person2.getId()){ return person1.getId()-person2.getId(); }else{ return person1.getName().compareTo(person2.getName()); } }); for(Person person:persons){ System.out.println(person); } } } 结果: Person{id=10, name='Tom'} Person{id=15, name='Com'} Person{id=20, name='Alice'} Person{id=20, name='Bob'}
1.5 方法的引用
1.5.1方法的引用介绍
-
当我们需要使用接口的方法时,如果我们之前实现过类似的功能方法或者接口,我们可以利用函数引用的方式传入该方法,从而使用该方法来完成我们所要实现的接口方法功能
-
方法引用的唯一用途是支持Lambda的简写
-
例如以下
persons.forEach(value-> System.out.println(value));
persons.forEach(System.out::println);
-
方法引用要求:
- 参数数量和类型要与需要传入接口中定义的一致
- 接口的抽象方法没有返回值,引用的方法可以有返回值也可以没有
- 返回值类型要与需要传入接口中定义的一致
- 对于泛型方法泛型的声明与正常方式相同
- 静态方法:
类名::<泛型声明>静态方法
- 成员方法:
类名::<泛型声明>静态方法
- 构造方法:
类名<泛型声明>::new
- 静态方法:
1.5.2 方法引用分类
类别 | 使用形式 |
---|---|
静态方法引用 | 类名 :: 静态方法名 |
实例方法引用 | 对象名(引用名) :: 实例方法名 |
类方法引用 | 类名 :: 实例方法名 |
构造方法引用 | 类名 :: new |
(1)静态方法引用
-
静态方法引用:引用比较的静态方法,代替
comparator
接口方法public class LambdaTest { public static int comparePerson(Person person1, Person person2){ if(person1.getId()!=person2.getId()){ return person1.getId()-person2.getId(); }else{ return person1.getName().compareTo(person2.getName()); } } public static void main(String[] args) { LinkedList<Person> persons = new LinkedList(); ... persons.sort(LambdaTest02::comparePerson); ... persons.forEach(value-> System.out.println(value)); } }
-
引用静态方法创建线程
new Thread(pojo::say).start();
-
注意引用静态方法,在调用该接口时也会对类产生影响,相当于同时调用了该类的静态方法
public class LambdaTest { static class Pojo{ private String name; static String str = "初始静态变量"; public Pojo(String name) { this.name = name; } public Pojo(){ this("初始名字"); } String getName(){ return this.name; } void setName(String name){ this.name = name; } void setAs(Pojo pojo){ this.name = pojo.name; } void say(){ System.out.println(name+":哈哈"); } public static String sayStaticValue(){ System.out.println("Pojo:"+Pojo.str); return Pojo.str; } public static void setStaticValue(String str){ Pojo.str = str; } } public static void main(String[] args) { Pojo pojo = new Pojo(""); GreetingService greetingService = Pojo::setStaticValue; greetingService.say("引用了pojo的setStaticValue方法");//引用了pojo的set方法 System.out.println("==="); System.out.println(Pojo.str); new Thread(Pojo::sayStaticValue).start();//Pojo:引用了pojo的setStaticValue方法 } }
等价于》》》GreetingService greetingService = (str)->Pojo.setStaticValue(str);
(2)实例方法引用
-
通过引用实例的方法来实现接口的功能
public class LambdaTest { public static int comparePerson(Person person1, Person person2){ if(person1.getId()!=person2.getId()){ return person1.getId()-person2.getId(); }else{ return person1.getName().compareTo(person2.getName()); } } public static void main(String[] args) { LinkedList<Person> persons = new LinkedList(); ... persons.sort(new PersonComparator()::comparePerson); //实际上persons.sort((p1,p2)->new PersonComparator().comparePerson(p1,p2)) ... persons.forEach(value-> System.out.println(value)); } } class PersonComparator{ public int comparePerson(Person person1, Person person2){ if(person1.getId()!=person2.getId()){ return person1.getId()-person2.getId(); }else{ return person1.getName().compareTo(person2.getName()); } } }
-
注意引用对象实例的方法之后,在调用该接口时也会对实例产生影响,相当于同时调用了该对象的方法
public class LambdaTest { ... public static void main(String[] args) { Pojo pojo = new Pojo(); GreetingService greetingService = pojo::setName; greetingService.say("引用了pojo的set方法"); System.out.println("==="); System.out.println(pojo.getName());//引用了pojo的set方法 } }
等价于》》》GreetingService greetingService = msg->pojo.setName(msg);
-
如果在类中成员方法内容上可以使用
this
关键字与super
关键字进行引用-
this
:表示使用该对象的方法进行引用public class MethodRefTest { public void say(){ System.out.println("hello."); } public void startThread(){ new Thread(this::say).start(); } public static void main(String[] args) { new MethodRefTest().startThread();//hello. } }
-
super
:表示使用父类的方法进行引用class Son extends MethodRefTest{ public void startThreadBySon(){ new Thread(super::say).start(); } public static void main(String[] args) { new Son().startThreadBySon(); } }
-
(3)类方法的引用
-
这里使用的是:类名::实例方法名
-
对象方法引用
class Person{ ... public int compareToAnother(Person person2){ if(this.getId()!=person2.getId()){ return this.getId()-person2.getId(); }else{ return this.getName().compareTo(person2.getName()); } } ... } public class LambdaTest02 { public static void main(String[] args) { LinkedList<Person> persons = new LinkedList(); ... persons.sort(Person::compareToAnother); //实质上为persns.sort((Person person1, Person person2) -> person1.compareToAnother(perosn2) ); persons.forEach(value-> System.out.println(value)); } }
- 首先要说明的是,方法引用不是方法调用。
compareToAnother
一定是某个实例调用的,该实例就被作为lambda表达式的第一个参数,然后lambda表达式剩下的参数作为compareToAnother
的参数,这样compareToAnother
正好符合lambda表达式的定义 - 或者也可以这样理解:
(Person person1, Person person2) -> { person1.compareToAnother(perosn2) }
需要当前对象调用,然后与另外一个对象比较,并且返回一个int值。可以理解为lambda表达式的第一个参数 person1 赋值给当前对象, 然后person2赋值给 other对象,然后返回int值。
- 首先要说明的是,方法引用不是方法调用。
(4)构造方法引用
-
构造方法的引用,实质上是调用了构造方法
interface CreateInter{ Pojo create(String name); } public class LambdaTest { public static void main(String[] args) { CreateInter createInter = Pojo::new; Pojo pojo = createInter.create("这是我的新名字"); System.out.println(pojo.getName());//这是我的新名字 } }
- 该示例代码调用了
public Pojo(String name)
构造方法
- 该示例代码调用了
-
构造数组对象
interface StringArrCreatable { public String[] createStringArr(int length); } class Test { public static void main(String[] args) { StringArrCreatable t2 = String[]::new; //实际上 StringArrCreatable t2 = (length) -> new String[length]; String[] arr = t2.createStringArr(5); System.out.println(arr.length);// } }
1.6 函数式接口使用
1.6.1 常用函数式接口
- jdk中提供了非常多的函数式接口,大多封装在
java.util.funcation
中
(1)Supplier
接口
-
该接口只包含一个无参方法:
T get()
,用于获得一个对象,源码:@FunctionalInterface public interface Supplier<T> { /** * Gets a result. * * @return a result */ T get(); }
-
该接口用来获取一个泛型参数指定类型的对象数据:
public class SupplierTest { public static<T> T getString(Supplier<T> supplier){ return supplier.get(); } public static void main(String[] args) { String str = getString(()->"Hello,"+"world."); // 实际上相当于: // String str = getString(new Supplier<String>() { // @Override // public String get() { // return "Hello,"+"world."; // } // }); System.out.println(str);//Hello,"+"world. } }
(2)Consumer
接口
-
该接口包含一个accept方法用于接收泛型指定的类型对象,然后使用该对象;还包含一个andThen默认方法用于下一步操作,实现组合操作的过程,源码:
@FunctionalInterface public interface Consumer<T> { /** * Performs this operation on the given argument. * @param t the input argument */ void accept(T t); default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; } }
-
使用
accept
方法public class ConsumerTest { public static void display(String name, Consumer<String> consumer) { consumer.accept(name); } public static void main(String[] args) { display("你好,世界", System.out::println);//你好,世界 //实际上相当于: display("你好,世界", (String o)->{ System.out.println(o); }); } }
-
使用
andThen
方法public class ConsumerTest { public static void method(String name, Consumer<String> first,Consumer<String> second) { first.andThen(second).accept(name); //实际上相当于: //frist.accpet(name); //second.accept(name); } public static void main(String[] args) { method("Java:", s-> System.out.println(s.toUpperCase()), s-> System.out.println(s.toLowerCase())); } }
(3)Predicate
接口
-
Predicate
接口为断言式接口,主要用来进行条件判断,源码:@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); } @SuppressWarnings("unchecked") static <T> Predicate<T> not(Predicate<? super T> target) { Objects.requireNonNull(target); return (Predicate<T>)target.negate(); } }
-
test
方法为测试方法,根据传入参数与方法的实现返回测试布尔数据结果Predicate<Integer> bigInteger = (n)->n.compareTo(1000000)>0; System.out.println(bigInteger.test(10));//false System.out.println(bigInteger.test(1000000000));//true
-
and
、negate
、or
方法用于传入两个Predicate接口实例,进行条件判断(分别对应&&
,!
,|
)Predicate<Integer> bigInteger = (n) -> n.compareTo(100) > 0; Predicate<Integer> evenInteger = (n) -> n % 2 == 0; evenInteger.and(bigInteger).test(1000);//true evenInteger.and(bigInteger).test(100);//false evenInteger.and(bigInteger).test(1001);//false evenInteger.negate().and(bigInteger).test(1001)//true evenInteger.or(bigInteger.negate()).test(102)//true
-
isEqual
静态方法提供了对传入对象的相等判断,如果传入对象为空,返回测试参数是否为空,否则调用传入对象的equals
方法Predicate.isEqual("String").test("String")//true Predicate.isEqual(null).test(null);//true Predicate.isEqual(null).test("String");//false Predicate.isEqual("String").test(null)//false
-
not
静态方法用于根据传入的Predicate实例创造‘否命题’,并返回新的Predicate实例
(4)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; } }
-
apply
方法用于将传入的对象,转换为其他类型的对象Function<Integer,String> integerToString = (integer)->"String:"+String.valueOf(integer); System.out.println(integerToString.apply(256));//String:256
-
andThen
方法将当前接口实例与传入的Function接口组合操作Function<Integer,String> integerToString = (integer)->String.valueOf(integer); Function<String,Double> stringToDouble = (str)->Double.valueOf(str); System.out.println(integerToString.andThen(stringToDouble).apply(1));//1.0
-
compose
方法与andThen相反,用于先用传入的Function接口进行转换再使用本接口进行转换操作System.out.println(stringToDouble.compose(integerToString).apply(1));