上一篇文章java8 lambda表达式详解(一)主要讲到了单独表达式和块儿lambda表达式,这篇文章里,会对更多的lambda表达式特性进行讲解!
1 泛型函数式接口
lambda表达式本身不能指定类型参数,所以当然不能是泛型,但是,与lambda表达式关联的函数式接口,是可以使用泛型的!现在考虑这样一种情况,
下面给出代码示例
interface ForMethodInterface {
void print(List<Integer> integerList);
}
public static void main(String[] args) {
String test1 = "hello lambda";
FunctionInterfaceT<String> ft = (test) -> {
return test.substring(1,3);
};
System.out.println(ft.getT(test1));
}
上面的例子很容易理解,下面的问题就是,如何更好的利用lambda表达式呢,无论是上一篇文章,还是这里,都是最简单的声明,而封装呢?该怎么做,比如,定义一个方法,参数就是第一个lambda表达式,其实,java8的集合类,都已经引入了此类方法,比如ArrayList
/**
* 打印lists集合内元素
* @param args
*/
public static void main(String[] args) {
ArrayList<Integer> lists = new ArrayList<Integer>();
lists.add(1);
lists.add(2);
//从前的做法
for (Integer list : lists){
System.out.println(list);
}
//java8的做法
lists.forEach((list)-> System.out.println(list));
}
上面是从前和现在的操作集合的方法,目前java8的集合,已经都新增了这个forEach()方法,这个方法的内部结构为
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int expectedModCount = modCount;
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
action.accept(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
其实很简单,里面就一个for循环,只不过,循环里执行的动作是类似于这样的,
(list)-> System.out.println(list)
lambda表达式而已.如果想要封装一个类似的方法,只要看这个就可以.
3 lambda表达式和变量捕获
在lamba表达式中,可以访问其作用域意以外的变量,比如,作用域意外的实力或者静态变量等,当这些变量时,会产生一种特殊情况,称为变量捕获,这种情况下,lambda表达式式只能使用实质上final定义的局部变量,实际上final的变量是指在第一次赋值以后,值不在发生变化的量,没有必要显示的将这种变量声明为final.
lambda表达式不能修改外层作用域的局部变量,修改的话会移除其实质上的final属性,从而使这次捕获变量不合法.下面演示一下实质上的final和可变局部变量的区别:
public static void main(String[] args) {
String test = "hello lambda";
FunctionInterfaceT fi = (str)->{
test = test + str;
return test;
};
}
这样编译器是无法通过编译的!
4 方法引用
如果像创建方法引用,可以使用类似这样的语法:ClassName::methonName,类名和方法中间,隔着两个;这种用法与lambda表达式相关,因为其也需要由兼容的函数式接口构成的目标类上下文:
public interface ForStaticUse {
String subString(String str);
}
public class LambdaS2 {
public static String sunStr(String str){
return str.substring(0,3);
}
}
public class MyStaticUseDemo {
private static String subStr(ForStaticUse fsu,String str){
return fsu.subString(str);
}
public static void main(String[] args) {
String test = "hello lambda";
String outStr;
outStr = subStr(LambdaS2::sunStr,test);
System.out.println(outStr);
}
}
除了自己定义函数式接口以外,java提供了一些预定义的函数式接口.这样我们就不需要大量的去定义函数式接口了!接口的定义在
import java.util.function
下面说明几个重要的函数式接口:
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
/**
* 返回一元操作运算,其实只是返回传进来的值
* @param <T> the type of the input and output of the operator
* @return a unary operator that always returns its input argument
*/
static <T> UnaryOperator<T> identity() {
return t -> t;
}
}
类似的还有,BinaryOperator<T>对类型为T的两个对象应用操作,结果类型也为T,方法为apply()
Consumer<T>,对类型为T的对象进行操作,包含的方法为accept();
Function<T,R>,对类型为T的对象操作,结果是R类型的返回值,包含的方法为accept();
其他的还有很多,不在这里一一列举,其实,想要让lambda表达式,真正的融入到自己的体系中,是需要不断的练习,不断的思考的!要不,只能是徒有其表!