Lambda表达式由参数、箭头和主体组成
(Apple a1,Apple a2)-> a1.getWeight().compareTo(a2.getWeight());
1.lambda是什么
可以把Lambda表达式理解为简洁的表示可传递的匿名函数一种方式:它没有名称,但是它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表
(String s)->s.length(); // 参数类型为Sting,返回值为int型;lambda 没有return语句,隐含return
(Apple a) ->a.getWeight()>150; //参数类型为Apple,返回值为boolean类型(判断苹果的重量是否大于150g)
(int x,int y)->{
System.out.println("Result:");
System.out.println(x+y);
}; //参数类型为两个int类型数据,没有返回值(void);lambda可以包含多行语句
()->42; //没有参数,返回值为int类型
(Apple a1,Apple a2) ->a1.getWeight().compareTo(a2.getWeight()); // 参数为2个Apple类型数据,返回值为int型
2.函数式接口:就是只定义一个抽象方法的接口
public interface Runnable{
void run();
}
Lambda允许你直接以内联的形式为函数式接口的抽象方法提供实现并把整个表达式作为函数式接口的实例,即lambda是函数式接口一个具体实现的实例。
public static void main(String[] args){
Runnable r1 = ()->System.out.println("Hello World 1");
Runnable r2 = new Runnable() {
@Override
public void run() {
System.out.println("Hello World 2");
}
};
process(r1); //使用lambda表达式
process(r2); //使用匿名类
process(()->System.out.println("Hello world 3")); //使用直接传递的lambda打印
}
public static void process(Runnable r){
r.run();
}
关于@FunctionalInterface
该注解不是必须的,如果一个接口符合"函数式接口"定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错
3.函数描述符:函数式接口的抽象方法的签名称为,这种抽象方法叫做函数描述符
4.常用函数式接口
(1)Predicate
函数描述符为: T->boolean 表示参数为泛型,返回值为boolean类型,当你需要表示一个需要涉及类型T的布尔表达式时,就可以使用这个接口。
//创建一个函数式接口
public interface Predicate<T> {
boolean test(T t);
}
//抽象方法
public static <T> List<T> filter(List<T> list,Predicate<T> p){
List<T> results = new ArrayList<>();
for (T s:list){
if(p.test(s)){
results.add(s);
}
}
return results;
}
//调用方法(分步骤)
Predicate<String> nonEmptyStringPredicate = (String s )-> !s.isEmpty();
List<String> nonEmpty = filter(listOfStrings,nonEmptyStringPredicate);
//也可以写成
List<String> nonEmpty = filter(listOfStrings, (String s )-> !s.isEmpty(););
注意:属于java.util.function.Predicate<T>
(2)Consumer
函数描述符为:T->void 表示参数类型为泛型,无返回值,当你需要访问T类型的对象,并对其执行某些操作,但不需要返回值时就使用这个接口。
//创建函数式接口
public interface Consumer<T> {
void accept(T t);
}
//抽象方法
public static <T> void forEach(List<T> list,Consumer<T> c){
for(T i:list){
c.accept(i);
}
}
//调用方法
forEach(Arrays.asList(1,2,3,4,5),(Integer i) -> System.out.println(i));
注意:属于java.util.function.Consumer<T>
(3)Function
函数描述符:T->R,表示参数类型为泛型,返回值类型也为泛型,当你需要将输入的对象的信息映射到输出就可以使用这个接口,比如输入一个对象“苹果”,想要得到它的重量,就可以使用这个函数。换句话说就是输入一个泛型,可以得到另一个泛型的值。
//创建函数式接口
public interface Function<T,R> {
R apply(T t);
}
//抽象方法
public static <T,R> List<R> map(List<T> list,Function<T,R> f){
List<R> result = new ArrayList<>();
for(T s:list){
result.add(f.apply(s));
}
return result;
}
//调用方法
List<Integer> list =map(Arrays.asList("Predicate","Consumer","Function"),(String s)->s.length());
注意:属于java.util.function.Function<T,R>
5.原始类型特化
java有两种类型,引用类型和基本类型,由于泛型只能绑定到引用类型,这是由于泛型内部的实现方式导致的。而java中又有一个自动装箱和自动拆箱机制 ,这两种操作是自动完成的,所以我们使用上面提到的函数式接口时,就会在性能上面付出很大的代价。
装箱:将原始类型(基本类型)转换为对应的引用类型
拆箱:将引用类型转换为对应的基本类型
回顾下之前的知识:基本数据类型和引用类型的区别主要在于基本数据类型是分配在栈上的,而引用类型是分配在堆上的
比如看下面一段代码:
Predicate<Integer> oddNumbers= (Integer i) ->i%2==1;
oddNumbers.test(1000);
这段代码就将参数10000自动装箱到一个Integer对象中,这时候我们要避免将int型自动装箱至Integer对象中,就可以使用Predicate<T>的原始类型特化接口IntPredicate
IntPredicate newNumbers=(int i)->i%2==0;
newNumbers.test(1000);
这段代码中参数1000仍为int类型。
网友评论