风也温柔

计算机科学知识库

java回调实现 Java-深入浅出lambda表达式

  内容导读

  表达式是java8引入的新功能,它是一个可传递的代码块,可以在以后执行一次或多次。在java里实现回调,基本都是通过接口来完成的。这个稍微有点不符合我们的习惯用法,类的实例方法,应该是不能直接调用的。当使用分隔符时,编译器会自动推导应该用哪种方式。在实际的业务开发中,在使用表达式的时候java回调实现 Java-深入浅出lambda表达式,会遇到报错。在表达式中,只能引用值不会改变的变量。

  什么是表达式

  表达式是java8引入的新功能,它是一个可传递的代码块,可以在以后执行一次或多次。

  接口与回调

  在讲解表达式之前,我们先讲一下接口。在java里实现回调,基本都是通过接口来完成的。代码如下:

  <pre>public interface ActionListener {
void actionPerformed(ActionEvent event);
}</pre>

  <pre>class TimePrinter implements ActionListener {
public void actionPerformed(ActionEvent event) {
System.out.println("At the tone, the time is " + new Date());
Toolkit.getDefaultToolkit().beep();
}
}</pre>

  <pre>ActionListener listener = new TimePrinter();
Timer t = new Timer(10000, listener);</pre>

  通过接口,我们可以实现将代码块封装到一个类里面,在需要调用的时候执行接口方法就可以了。

  表达式语法

  回到表达式。如果需要比较一个字符串是否比另一个字符串长,可以用表达式:

  <pre>(String first, String second)
-> first.length() - second.length()</pre>

  可以看到,表达式的形式为:参数,箭头(->)以及一个表达式。

  当表达式很复杂时,可以使用{}块java回调实现,在里面写复杂逻辑。例如:

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

  当表达式没有参数时,也需要使用空括号,就像无参数方法一样:

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

  用表达式实现回调

  之前用接口实现的逻辑,现在就可以用表达式来重新实现一遍了:

  <pre>Timer t = new Timer(1000, event -> {
System.out.println("At the tone, the time is " + new Date());
Toolkit.getDefaultToolkit().beep();
});</pre>

  表达式,还可以保存到变量里,放在以后执行:

  <pre>@FunctionalInterface
public interface BiFunction {
/**

  • Applies this function to the given arguments.
    *
  • @param t the first function argument
  • @param u the second function argument
  • @return the function result
    */

R apply(T t, U u);
}</pre>

  <pre>import java.util.function.BiFunction;
public class Main {
public static void main(String[] args) {
BiFunction bi = (x, y) -> {
return x + y;
};
System.out.println(bi.apply("youni", " youzan"));
}
}
/*output:
youni youzan
*/</pre>

  总结一下,表达式的优缺点。

  优点:直观明了,可读性好。

  缺点:不能复用逻辑。

  方法引用

  如何将现成的方法放到表达式里?

  <pre>Timer t = new Timer(1000, event -> System.out.println(event));</pre>

  以上程序可以简写成:

  <pre>Timer t = new Timer(1000, System.out::println);</pre>

  可以用::操作分隔方法名与对象或类名。主要有3种情况:

  ::

  参数直接透传到对应对象的实例方法,例如.out::(x) 等价于 x -> .out.(x)。

  Class::

  参数直接透传到具体类的静态方法,例如(x, y) -> Math::pow(x, y) 等价于(x,y) -> Math.pow(x, y)。

  Class::

  这个稍微有点不符合我们的习惯用法,类的实例方法,应该是不能直接调用的。

  这里就涉及到编译器的推导功能了。当使用分隔符时,编译器会自动推导应该用哪种方式。

  例如(x, y) -> ::(x, y) 等价于 (x, y) -> (y)。

  函数式接口

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

  <pre>public Timer(int delay, ActionListener listener);</pre>

  <pre>public interface ActionListener extends EventListener {
/**

  • Invoked when an action occurs.
    */

public void actionPerformed(ActionEvent e);
}</pre>

  <pre>Timer t = new Timer(1000, event ->
System.out.println("The time is " + new Date()));
t.start();</pre>

  这里的就是函数式接口。

  变量作用域

  在实际的业务开发中,在使用表达式的时候,会遇到报错。例如一下代码。

  <pre>public class FooService {
@Resource
private FooDao fooDao;
@Resource
private BarDao barDao;
public void foo(boolean addBar) {
List foos = fooDao.listFoo();
Bar bar = null;
if (addBar) {
bar = barDao.getBar();
}
foos.stream().forEach(e -> {
if (bar == null) {
update(e);
}
});
}
private void update(Foo e) {
}
}</pre>

  ide会提示: used in be final or final。

  什么意思?这里就涉及到表达式的变量作用域了。

  表达式有3个部分:

  一个代码块;

  参数;

  自由变量的值,这是指非参数而且不在代码中定义的变量。

  在表达式中,只能引用值不会改变的变量。

  上面的报错,就是bar变量声明以后可能会有修改java回调实现,导致了报错。

  参考资料:

  《Java核心技术 卷I》

  

  文章来源:http://www.toutiao.com/a6534781436271002126/