java lambda表达式

lambda表达式

lambda表达式是一个可传递的代码块,可以在以后执行一次或多次.

Java1.8新特性lambda表达式还没有出来之前,在Java中是不能直接传递代码段,因为java是一种面向对象的语言,所以必须构造一个对象,这个对象的类需要一个方法能包含所含有的代码.

lambda表达的语法

比如之前想实现一个按长度而不是默认的字段顺序对字符串排序,可以向sort方法穿入一个Comparator对象:

public class LengthComparator implements Comparator<String> {
    @Override
    public int compare(String o1, String o2) {
        return o1.length() - o2.length();
    }
    public static void main(String[] args) {
        String[] strs = {"A","BB"};
        Arrays.sort(strs,new LengthComparator());
    }
}

第一个lambda表达式,参数,箭头(->)以及一个表达式

(String first, String second) -> first.length() - second.length()

如果代码要完成的计算无法放在一个表达式中,就可以像写方法一样,把这些代码放在{}中,并包含显式的return语句.

(String first,String second) -> {
            if(first.length() < second.length()) return -1;
            else if(first.length() > second.length()) return 1;
            else return 0;
        }

即使lambda表达式没有参数,仍然要提供空括号,就想无参数方法一样:

() -> {
            for (int i = 100; i >= 0; i--) {
                System.out.println(i);
            }
        }

如果可以推导出一个lambda表达式的参数类型,则可以忽略其类型:

Comparator<String> comp = (first,second) -> first.length() - second.length();
等同于
Comparator<String> comp = (String first,String second) -> first.length() - second.length();

函数式接口

对于只有一个抽象方法的接口,需要这种接口的对象时,就可以提供一个lambda表达式.这种接口称为函数式接口(functional interface)

Array.sort方法,它的第二个参数需要一个Comparator实例,Comparator就是只有一个方法的接口,所以可以提供一个lambda表达式:

Arrays.sort(words,(first,second) -> first.length() - second.length());

在底层,Arrays.sort方法会接受实现了Comparator< String >的某个类的对象.在这个对象上调用compare方法会执行这个lambda表达式的体.

方法引用

如果已经有现成的方法可以完成你想要传递到其他代码的某个动作,例如,你希望只要出现一个定时器时间就打印这个事件对象.

Timer timer = new Timer(1000, e -> System.out.println(e));

但是,如果直接把println方法传到到Timer构造器就更好.

Timer timer = new Timer(1000, System.out::println);

表达式 System.out::println 是一个方法引用(method reference),等价于lambda表达式 x -> System.out.println(x).
还有例子,比如要对字符串进行排序,不考虑字母的大小写,
Arrays.sort(strings,String::compareToIgnoreCase)
要用::操作符分割方法名与对象或类名.
存在以下3中情况

  1. object::instanceMethod
  2. Class::staticmethod
  3. Class::instanceMethod

在前2中情况中,方法引用等价于提供方法参数的lambda表达式.
System.out::println 等价于 x -> System.out.println(x)
Math::pow 等价于 (x,y) -> Math:pow(x,y)

对于第3种情况,第一个参数会成为方法的目标.
String::compareToIgnoreCase 等价于 (x,y) -> x.compareToIgnoreCase(y)

构造引用

构造器引用与方法引用类似,例如Person::new是Person构造器的一个引用.哪一个构造器,取决于上下文.假如有一个字符串列表,可以把它转换为一个Person对象数组,为此要在各个字符串上调用构造器,调用如下:

ArrayList<String> names =...;
Stream<Person> stream = names.stream().map(Person::new);
List<Person> people = stream.collect(Collectors.toList());

可以用数组类型简历构造器引用.例如 int[]::new 是一个构造器引用,它有一个参数:即数组的长度,等价于 x -> new int[x]
Java有一个限制,无法构造泛型类型T的数组,数组构造器引用对于克服这个限制很有用.
表达式 new T[n]是会产生错误,因为这会改为new Object[n].
对于开发类库的人来说,这是一个问题.例如,假设我们需要一个Person对象数组.Stream接口有一个toArray方法可以返回Object数组:
Object[] people = stream.toArray();
用户希望得到一个Person引用数组,而不是Object引用数组.流库利用构造器引用解决了这个问题.可以把Person[]::new传入toArray方法:
Person[] people = stream.toArray(Person[]::new)
toArray方法调用这个构造器来得到一个真确类型的数组.然后填充这个数组并返回.

变量作用域

lambda表达式有3个部分:
    1.一个代码块;
    2.参数;
    3.自由变量的值,这里值非参数而且不再代码中定义的变量.
public static void repeatMessage(String text, int delay) {
        ActionListener listener = event -> {
            System.out.println(text);
            Toolkit.getDefaultToolkit().beep();
        };
        new Timer(delay, listener).start();
    }

repeatMessage(“Hello”,1000);
来看这一段代码中的变量text,这个变量并不是在lambda表达式中定义的,实际上,这是repeatMessage方法的一个参数变量.
这里 存在一个问题,lambda表达式的代码可能会在repeatMessage调用返回很久以后才运行,而那时这个参数变量已经不存在了.
这个lambda表达式有1个自由变量text,表示lambda表达式的数据结构必须存储在自由变量的值,在这里就是字符串”Hello”,它被lambda表达式捕获(captured).(可以把一个lambda表达式转换为包含一个方法的对象,这样自由变量的值就会赋值到这个对象的实例变量中)
在lambda表达式中,有一个中重要的限制,只能引用值不会改变的变量.

  1. 在lambda表达式中改变变量的,并发执行多个动作时就会不安全.
  2. 如果在lambda表达式中引用变量,而这个变量可能在外部改变,也是不合法的.

规则:lambda表达式中捕获的变量必须实际上是最终变量(effectively final).

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值