java 8中将支持Lambda语法,在JDK 8开发者预览版发布之后,Java社区的Lambda项目又在JDK中添加了Lambda功能。
1.Lambda表达式
lambda表达式是匿名函数(anonymous functions)
是对那些内部只含有一个方法的接口的实例化
省去??碌睦嗟纳?饔锞?/p>
1.1实例:
实现一个接口
在java 8之前,我们实现一个接口,如下:
public class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("hello!");
}
}
然后在新线程中开启:
MyRunnable r = new MyRunnable();
new Thread(r).start();
使用内部类
将上面的改成内部类实现,可以省略部分声明类的代码:
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("hello!");
}
};
new Thread(r).start();
使用匿名内部类
如果还想省,可以使用匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello!");
}
}).start();
使用Lambda表达式
使用lambda表达式,可以更简洁:
Runnable r = () -> System.out.println("hello!");
new Thread(r).start();
2.在什么地方使用lambda表达式
lambda表达式只能用在给变量赋值,而这个变量的类型必须是函数式接口(functional interface)。
更多详情,见lambda translation
2.1函数式接口(functional interface)
一个函数式接口只有一个抽象方法。
java运行时中的函数式接口有:Runnable,Callable,Comparator,TimeTask
java 8 中称这种为“Single Abstract Method”(SAM)类型
见java 8中的Runnable接口的声明:
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
2.2 Lambda表达式的语法
以Runnable接口为例子:
lamba
lambda表达式分为两部分:
1.第一个部分是()括号内的,其实就是函数式接口里的抽象方法的方法签名。而Runnable接口的run()方法刚好没有参数,所以是空的。(注意:如果方法的参数只有一个,那么可以省略括号)
2.第二个部分是箭头后面的语句,那个是抽象方法的具体实现。如果有多条语句,需要用花括号括起来。
下面是多条语句:
Runnable r = () -> {
System.out.println("one line");
System.out.println("two line");
};
3. 自定义函数式接口
定义一个函数式接口,必须用@FunctionalInterface注解标记,如下:
@FunctionalInterface
public interface SimpleInterface {
//只有一个抽象方法,这里我们声明一个有参方法
public void doAdd(int a,int b);
}
用lambda表达式实现这个接口:
public class UseSimpleInterface {
public static void main(String[] args) {
SimpleInterface obj = (v1,v2) -> {
int result = v1 v2;
System.out.println( "result:" result );
};
obj.doAdd(2,3);
}
}
结果:
result:5
4.使用java 8内置的函数式接口
4.1 Runnable接口
未使用lambda:
public class UseRunnable {
public static void main(String[] args) {
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("Running Thread 1");
}
};
Runnable r2 = new Runnable() {
@Override
public void run() {
System.out.println("Running Thread 2");
}
};
new Thread(r1).start();
new Thread(r2).start();
}
}
使用lambda后:
public class UseRunnable {
public static void main(String[] args) {
Runnable r1 = () ->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Running Thread 1");
};
Runnable r2 = () ->{
System.out.println("Running Thread 2");
};
new Thread(r1).start();
new Thread(r2).start();
}
}
4.2 Comparator接口
未使用Lambda之前:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class UseComparator {
public static void main(String args[]){
List strings = new ArrayList();
strings.add("AAA");
strings.add("bbb");
strings.add("CCC");
strings.add("ddd");
strings.add("EEE");
//Simple case-sensitive sort operation
Collections.sort(strings);
System.out.println("Simple sort");
for(String str: strings){
System.out.println(str);
}
// 使用匿名类来实现大小写不敏感的排序
Collections.sort(strings, new Comparator() {
@Override
public int compare(String str1, String str2) {
return str1.compareToIgnoreCase(str2);
}
});
System.out.println("Sort with comparator");
for(String str: strings){
System.out.println(str);
}
}
}
使用lambda后:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class UseComparator {
public static void main(String args[]){
List strings = new ArrayList();
strings.add("AAA");
strings.add("bbb");
strings.add("CCC");
strings.add("ddd");
strings.add("EEE");
//Simple case-sensitive sort operation
Collections.sort(strings);
System.out.println("Simple sort");
for(String str: strings){
System.out.println(str);
}
//使用匿名类来实现大小写不敏感的排序
Comparator comparator = (str1,str2) ->{
return str1.compareToIgnoreCase(str2);
};
Collections.sort(strings, comparator);
System.out.println("Sort with comparator");
for(String str: strings){
System.out.println(str);
}
}
}
4.3 使用lambda遍历集合
未使用lambda之前:
package org.example.java8;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
public class Main {
public static void main(String args[]){
List strings = new ArrayList();
strings.add("AAA");
strings.add("bbb");
strings.add("CCC");
strings.add("ddd");
strings.add("EEE");
Collections.sort(strings);
System.out.println("Simple sort");
// Traverse with for:each
for(String str: strings){
System.out.println(str);
}
Comparator comp = (str1, str2) ->
{
return str1.compareToIgnoreCase(str2);
};
Collections.sort(strings, comp);
System.out.println("Sort with comparator");
//Traverse with iterator
Iterator i = strings.iterator();
while (i.hasNext()) {
System.out.println(i.next());
}
}
}
因为List类实现了Iterable接口,所以ArrayList自然也实现了Iterable接口的foreach方法,ArrayList的foreach源代码如下:
default void forEach(Consumer super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
其中的Consumer接口是个函数式接口,源码如下:
@FunctionalInterface
public interface Consumer {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
/**
* Returns a composed {@code Consumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*
* @param after the operation to perform after this operation
* @return a composed {@code Consumer} that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default Consumer andThen(Consumer super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
所以,这里我们foreach中Comsumer接口中的accept抽象方法可以用lambda表达式来替代,如下:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Main {
public static void main(String args[]){
List strings = new ArrayList();
strings.add("AAA");
strings.add("bbb");
strings.add("CCC");
strings.add("ddd");
strings.add("EEE");
Collections.sort(strings);
System.out.println("Simple sort");
strings.forEach( str -> System.out.println(str));
Comparator comp = (str1, str2) ->
{
return str1.compareToIgnoreCase(str2);
};
Collections.sort(strings, comp);
System.out.println("Sort with comparator");
strings.forEach( str -> System.out.println(str));
}
}
4.4 使用Predicte接口过滤集合
将Predicte作为匿名类来过滤集合
Person.java:
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return this.name " (" this.age ")";
}
}
将Predicate作为内部类:
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public class PredicateTest {
public static void main(String args[]){
List people = new ArrayList<>();
people.add(new Person("Joe", 48));
people.add(new Person("Mary", 30));
people.add(new Person("Mike", 73));
Predicate pred = new Predicate() {
@Override
public boolean test(Person person) {
return (person.getAge() >= 65 );
}
};
for (Person person: people) {
if ( pred.test(person)){
System.out.println(person.toString());
}
}
}
}
对Predicate接口使用lambda表达式
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public class PredicateTest {
public static void main(String args[]){
List people = new ArrayList<>();
people.add(new Person("Joe", 48));
people.add(new Person("Mary", 30));
people.add(new Person("Mike", 73));
Predicate pred = p -> (p.getAge() >= 65 );
people.forEach( p -> {
if (pred.test(p)){
System.out.println(p.toString());
}
});
}
}
5.使用方法引用(method reference)
方法引用(method reference) 可以将某个方法当作参数传递给另外一个方法。
实例:
在Person.java中添加一个静态方法compareAges,如下:
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return this.name " (" this.age ")";
}
//比较年龄
public static int compareAges(Person p1,Person p2){
Integer age1 = p1.getAge();
return age1.compareTo(p2.getAge());
}
}
通过方法引用来将其作为参数传递给Collections.sort()方法:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class StaticMethodReference {
public static void main(String args[]){
List people = new ArrayList<>();
people.add(new Person("Joe", 48));
people.add(new Person("Mary", 30));
people.add(new Person("Mike", 73));
//使用方法引用,将其作为参数传递
Collections.sort(people, Person :: compareAges);
people.forEach(person -> System.out.println(person.toString()));
}
}
结果:
Mary (30)
Joe (48)
Mike (73)
我们也可以将compareAges()方法放到StaticMethodReference类中,而且去掉它的static,作为一个实例方法,如下:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class StaticMethodReference {
public static void main(String args[]){
List people = new ArrayList<>();
people.add(new Person("Joe", 48));
people.add(new Person("Mary", 30));
people.add(new Person("Mike", 73));
//使用方法引用,将其作为参数传递
StaticMethodReference mainClass = new StaticMethodReference();
Collections.sort(people, mainClass :: compareAges);
people.forEach(person -> System.out.println(person.toString()));
}
// 比较年龄
public int compareAges(Person p1,Person p2){
Integer age1 = p1.getAge();
return age1.compareTo(p2.getAge());
}
}
6.接口的中的默认方法(default method)
java 8 中接口可以定义静态方法和默认方法(default method),先说默认方法:
使用场景:
在java 8以前,如果一个接口要是需要添加方法,那么所有已经实现了这个接口的类就必须全部修改。为了避免这个问题,在java 8中,我们可以使用默认方法来实现添加方法,同时又不会影响已经实现类。因为其他的实现类可以重写这个默认方法,也可以不重写。
实例:
PersonInterface接口,其中定义了一个默认方法:
public interface PersonInterface {
String getName();
void setName(String name);
int getAge();
void setAge(int age);
// 定义默认方法,打印名字和年龄等信息
default String getPersionInfo(){
return getName() " (" getAge() ") ";
}
}
Person类,实现了PersonInterface接口:
public class Person implements PersonInterface {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
调用接口的默认方法:
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public class UseDefaultMethod {
public static void main(String args[]){
List people = new ArrayList<>();
people.add(new Person("Joe", 48));
people.add(new Person("Mary", 30));
people.add(new Person("Mike", 73));
Predicate pred = (p) -> p.getAge() > 65;
displayPeople(people, pred);
}
private static void displayPeople(List people,
Predicate pred) {
System.out.println("Selected:");
people.forEach(p -> {
if (pred.test(p))
{
//调用接口的默认方法
System.out.println( p.getPersionInfo());;
}
});
}
}
6.1 陷阱
public class ClassA {
public String getMessage(){
return "Hello";
}
}
public interface InterfaceA {
default String getMessage(){
return "Hola";
}
}
public class DemoClass extends ClassA implements InterfaceA {
public static void main(String[] args) {
System.out.println(new DemoClass().getMessage());
}
}
结果:
Hello
分析:
如果一个类从它的父类继承一个方法,又从它的父接口那里继承一个方法签名相同的方法。此时,这个类会继承这个父类的方法,而忽略掉这个父接口的方法。
当一个类通过类继承的方式继承的方法不可用的时候,则一个接口中与这个方法的签名相同的默认方法就会作为一个备用方法。
6.2 陷阱
有接口A,类B,A中定义的默认方法签名与B中的方法一样。类C继承了B,实现了A。则如何调用接口A中的默认方法?
方法如下:使用 InterfaceName.super.method()
public interface SimpleInterface {
default void getMessage(){
System.out.println("SimpleInterface");
}
}
public class ClassA {
public void getMessage(){
System.out.println("ClassA");
}
}
public class DemoClass extends ClassA implements SimpleInterface {
public void getMessage(){
SimpleInterface.super.getMessage();
System.out.println("Child");
}
public static void main(String[] args) {
SimpleInterface simpleInterface = new DemoClass();
simpleInterface.getMessage();
}
}
demo2
public class Java8Tester {
public static void main(String args[]){
Vehicle vehicle = new Car();
vehicle.print();
}
}
interface Vehicle {
default void print(){
System.out.println("I am a vehicle!");
}
static void blowHorn(){
System.out.println("Blowing horn!!!");
}
}
interface FourWheeler {
default void print(){
System.out.println("I am a four wheeler!");
}
}
class Car implements Vehicle, FourWheeler {
public void print(){
Vehicle.super.print();
FourWheeler.super.print();
Vehicle.blowHorn();
System.out.println("I am a car!");
}
}
6.3陷阱
当一个了实现一个有默认方法的接口时,可以不重写默认方法。但是,如果有两个接口的默认方法相同,而类C由同时implements这两个接口,则类C必须重写默认方法,否则编译错误。
public interface InterfaceA {
default void getMessage(){
System.out.println("InterfaceA");
}
}
public interface InterfaceB {
default void getMessage(){
System.out.println("InterfaceB");
}
}
public class DemoClass implements InterfaceB ,InterfaceA{
public static void main(String[] args) {
System.out.println("xxx");
}
}
运行:
编译报错。
解决办法:实现接口中的方法。如果要调用某个接口的方法,则需要显示的调用。
public class DemoClass implements InterfaceB ,InterfaceA{
public void getMessage(){
InterfaceA.super.getMessage();
}
public static void main(String[] args) {
System.out.println("xxx");
}
}
7.接口中的静态方法(static method)
接口中也可以定义静态方法,和在普通类中定义静态方法是一样的。
注意:接口中的静态方法不能与默认方法签名相同,否则编译错误
实例:
PersonInterface接口中定义了一个静态方法:
public interface PersonInterface {
String getName();
void setName(String name);
int getAge();
void setAge(int age);
//定义静态方法,打印名字和年龄等信息
static String getPersionInfo(Person person){
return person.getName() " (" person.getAge() ") ";
}
}
Person类,实现了PersonInterface:
public class Person implements PersonInterface {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
调用接口的静态方法:
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public class UseStaticMethod {
public static void main(String args[]){
List people = new ArrayList<>();
people.add(new Person("Joe", 48));
people.add(new Person("Mary", 30));
people.add(new Person("Mike", 73));
Predicate pred = (p) -> p.getAge() > 65;
displayPeople(people, pred);
}
private static void displayPeople(List people,
Predicate pred) {
System.out.println("Selected:");
people.forEach(p -> {
if (pred.test(p))
{
//调用接口的静态方法
System.out.println( PersonInterface.getPersionInfo(p));;
}
});
}
}