本文源码

Lambda 管中窥豹

可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。

Lambda表达式可以让你十分简明地传递代码。


Lambda组成结构

  • 参数列表——这里它采用了Comparator中compare方法的参数,两个Apple。
  • 箭头——箭头->把参数列表与Lambda主体分隔开。
  • Lambda主体——比较两个Apple的重量。表达式就是Lambda的返回值了。

Lambda的基本语法是

(parameters) -> expression

或(请注意语句的花括号)

(parameters) -> { statements; }

Java8先前:

Comparator<Apple> byWeight = new Comparator<Apple>() {public int compare(Apple a1, Apple a2){return a1.getWeight().compareTo(a2.getWeight());}
};

Java8之后(用了Lambda表达式):

Comparator<Apple> byWeight = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());

Java 8中有效的Lambda表达式

(String s) -> s.length()(Apple a) -> a.getWeight() > 150(int x, int y) -> {System.out.println("Result:");System.out.println(x+y);
}() -> 42(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())

使用案例 Lambda示例
布尔表达式 (List list) -> list.isEmpty()
创建对象 () -> new Apple(10)
消费一个对象 (Apple a) -> {System.out.println(a.getWeight());}
从一个对象中选择/抽取 (String s) -> s.length()
组合两个值 (int a, int b) -> a * b
比较两个对象 (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())

在哪里以及如何使用Lambda

你可以在函数式接口上使用Lambda表达式。

在上面的代码中, 你可以把Lambda 表达式作为第二个参数传给filter 方法, 因为它这里需要Predicate<T>,而这是一个函数式接口

函数式接口

Predicate仅仅定义了一个抽象方法

public interface Predicate<T>{boolean test (T t);
}

一言以蔽之,函数式接口就是只定义一个抽象方法的接口

Java API中的一些其他函数式接口,

//java.util.Comparator
public interface Comparator<T> {int compare(T o1, T o2);
}//java.lang.Runnable
public interface Runnable{void run();
}//java.awt.event.ActionListener
public interface ActionListener extends EventListener{void actionPerformed(ActionEvent e);
}//java.util.concurrent.Callable
public interface Callable<V>{V call();
}//java.security.PrivilegedAction
public interface PrivilegedAction<V>{V run();
}

用函数式接口可以干什么呢?Lambda表达式允许你直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例(具体说来,是函数式接口一个具体实现的实例)。

你用匿名内部类也可以完成同样的事情,只不过比较笨拙:需要提供一个实现,然后再直接内联将它实例化。下面的代码是有效的,因为Runnable是一个只定义了一个抽象方法run的函数式接口

Runnable r1 = () -> System.out.println("Hello World 1");
Runnable r2 = new Runnable(){public void run(){System.out.println("Hello World 2");}
};public static void process(Runnable r){r.run();
}process(r1);
process(r2);process(() -> System.out.println("Hello World 3"));

函数描述符

函数式接口的抽象方法的签名基本上就是Lambda表达式的签名。我们将这种抽象方法叫作函数描述符

为什么只有在需要函数式接口的时候才可以传递Lambda呢?语言设计者选择了现在这种方式,因为这种方式自然且能避免语言变得更复杂。

@FunctionalInterface
如果你去看看新的Java API,会发现函数式接口带有@FunctionalInterface的标注。这个标注用于表示该接口会设计成一个函数式接口。

如果你用@FunctionalInterface定义了一个接口,而它却不是函数式接口的话,编译器将返回一个提示原因的错误。

例如,错误消息可能是“Multiple non-overriding abstract methods found in interface Foo”,表明存在多个抽象方法。

请注意,@FunctionalInterface不是必需的,但对于为此设计的接口而言,使用它是比较好的做法。它就像是@Override标注表示方法被重写了。

把Lambda 付诸实践:环绕执行模式

资源处理(例如处理文件或数据库)时一个常见的模式就是打开一个资源,做一些处理,然后关闭资源。

这个设置和清理阶段总是很类似,并且会围绕着执行处理的那些重要代码。这就是所谓的环绕执行(execute around)模式

public static String processFile() throws IOException {try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {return br.readLine();//这就是做有用工作的那行代码}
}

把processFile的行为参数化。需要一种方法把行为传递给processFile,以便它可以利用BufferedReader执行不同的行为。

第1步:记得行为参数化

public static String processFile() throws IOException {try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {return br.readLine();//这就是做有用工作的那行代码}
}

转化成

String result = processFile((BufferedReader br) -> br.readLine());

第2步:使用函数式接口来传递行为

@FunctionalInterface
public interface BufferedReaderProcessor {String process(BufferedReader b) throws IOException;
}

现在你就可以把这个接口作为新的processFile方法的参数了:

public static String processFile(BufferedReaderProcessor p) throws IOException {…
}

第3步:执行一个行为

public static String processFile(BufferedReaderProcessor p) throws
IOException {try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {return p.process(br);}
}

第4步:传递Lambda

现在你就可以通过传递不同的Lambda重用processFile方法,并以不同的方式处理文件了。

处理一行:

String oneLine = processFile((BufferedReader br) -> br.readLine());

处理两行:

String twoLines = processFile((BufferedReader br) -> br.readLine() + br.readLine());

使用函数式接口

函数式接口的抽象方法的签名称为函数描述符。所以为了应用不同的Lambda表达式,你需要一套能够描述常见函数描述符的函数式接口

Predicate

java.util.function.Predicate<T>接口定义了一个名叫test的抽象方法,它接受泛型T对象,并返回一个boolean。

@FunctionalInterface
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);

Consumer

java.util.function.Consumer定义了一个名叫accept的抽象方法,它接受泛型T的对象,没有返回(void)。你如果需要访问类型T的对象,并对其执行某些操作,就可以使用这个接口。比如,你可以用它来创建一个forEach方法,接受一个Integers的列表,并对其中每个元素执行操作。

@FunctionalInterface
public interface Consummer<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,6,7), (Integer i)->System.out.println(i));

Function

java.util.function.Function<T, R>接口定义了一个叫作apply的方法,它接受一个泛型T的对象,并返回一个泛型R的对象。如果你需要定义一个Lambda,将输入对象的信息映射到输出,就可以使用这个接口(比如提取苹果的重量,或把字符串映射为它的长度)

@FunctionalInterface
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> list2 = map(Arrays.asList("","1234","asd"),(String s)->s.length());
System.out.println(list2);

原始类型特化

泛型(比如Consumer<T>中的T)只能绑定到引用类型。这是由泛型内部的实现方式造成的。因此,在Java里有一个将原始类型转换为对应的引用类型的机制。这个机制叫作装箱(boxing)。相反的操作,也就是将引用类型转换为对应的原始类型,叫作拆箱(unboxing)。

但这在性能方面是要付出代价的。装箱后的值本质上就是把原始类型包裹起来,并保存在堆里。因此,装箱后的值需要更多的内存,并需要额外的内存搜索来获取被包裹的原始值。

Java 8 提供特殊的函数式接口,以便在输入和输出都是原始类型避免自动装箱操作

public interface IntPredicate{boolean test(int t);
}//无装箱
IntPredicate evenNumbers = (int i) -> i % 2 == 0;
evenNumbers.test(1000);//装箱
Predicate<Integer> oddNumbers = (Integer i) -> i % 2 == 1;
oddNumbers.test(1000);

Java 8 中的常用函数式接口

//助记//喂,消费者,生产者,我们一起玩(fun)吧
Predicate,Consumer,Supplier,Function//数学 一元符,二元符
UnaryOperator,BinaryOperator//(喂,消费者,生产者)* 2
BiPredicate,BiConsumer,BiFunction
函数式接口 函数描述符 原始类型特化
Predicate<T> T->boolean IntPredicate, LongPredicate, DoublePredicate
Consumer<T> T->void IntConsumer, LongConsumer, DoubleConsumer
Function<T,R> T->R IntFunction<R>, IntToDoubleFunction, IntToLongFunction, LongFunction<R>, LongToDoubleFunction, LongToIntFunction, DoubleFunction<R>, ToIntFunction<T>, ToDoubleFunction<T>, ToLongFunction<T>
Supplier<T,R> ()->T BooleanSupplier, IntSupplier, LongSupplier, DoubleSupplier
UnaryOperator<T,R> T->T IntUnaryOperator, LongUnaryOperator, DoubleUnaryOperator
BinaryOperator<T,R> (T,T)->T IntBinaryOperator, LongBinaryOperator, DoubleBinaryOperator
BiPredicate<L,R> (L,R)->boolean -
BiConsumer<T,U> (T,U)->void ObjIntConsumer<T,R>, ObjLongConsumer<T,R>, ObjDoubleConsumer<T,R>
BiFunction<T,U,R> (T,U)->R ToIntBiFunction<T,U>, ToLongBiFunction<T,U>, ToDoubleBiFunction<T,U>

Lambda示例

使用案例 Lambda示例 对应的函数式接口
布尔表达式 (List<String> list) -> list.isEmpty() Predicate<List<String>>
创建对象 () -> new Apple(10) Supplier<Apple>
消费一个对象 (Apple a) -> { System.out.println(a.getWeight()); } Consumer<Apple>
从一个对象中选择/抽取 (String s) -> s.length() Function<String, Integer>或 ToIntFunction<String>
组合两个值 (int a, int b) -> a * b IntBinaryOperator
比较两个对象 (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()) Comparator或 BiFunction<Apple, Apple, Integer> 或 ToIntBiFunction<Apple, Apple>

异常

任何函数式接口都不允许抛出受检异常(checked exception)。如果你需要Lambda表达式来抛出异常,有两种办法:

  1. 定义一个自己的函数式接口,并声明受检异常,
  2. 把Lambda包在一个try/catch块中
@FunctionalInterface
public interface BufferedReaderProcessor {String process(BufferedReader b) throws IOException;
}BufferedReaderProcessor p = (BufferedReader br) -> br.readLine();
Function<BufferedReader, String> f = (BufferedReader b) -> {try {return b.readLine();}catch(IOException e) {throw new RuntimeException(e);}
};

类型检查、类型推断以及限制

类型检查

Lambda的类型是从使用Lambda的上下文推断出来的。上下文(比如,接受它传递的方法的参数,或接受它的值的局部变量)中Lambda表达式需要的类型称为目标类型

  • 首先,你要找出filter方法的声明。
  • 第二,要求它是Predicate(目标类型)对象的第二个正式参数。
  • 第三,Predicate是一个函数式接口,定义了一个叫作test的抽象方法。
  • 第四,test方法描述了一个函数描述符,它可以接受一个Apple,并返回一个boolean。
  • 最后,filter的任何实际参数都必须匹配这个要求。

同样的Lambda,不同的函数式接口

Comparator<Apple> c1 =
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());ToIntBiFunction<Apple, Apple> c2 =
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());BiFunction<Apple, Apple, Integer> c3 =
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());

特殊的void兼容规则

如果一个Lambda的主体是一个语句表达式, 它就和一个返回void的函数描述符兼容(当然需要参数列表也兼容)。例如,以下两行都是合法的,尽管List的add方法返回了一个boolean,而不是Consumer上下文(T -> void)所要求的void:

// Predicate返回了一个boolean
Predicate<String> p = s -> list.add(s);
// Consumer返回了一个void
Consumer<String> b = s -> list.add(s);

类型检查——为什么下面的代码不能编译呢?
你该如何解决这个问题呢?

Object o = () -> {System.out.println("Tricky example"); };

答案:Lambda表达式的上下文是Object(目标类型)。但Object不是一个函数式接口
为了解决这个问题,你可以把目标类型改成Runnable,它的函数描述符是() -> void:

Runnable r = () -> {System.out.println("Tricky example"); };

类型推断

Java编译器会从上下文(目标类型)推断出用什么函数式接口来配合Lambda表达式,这意味着它也可以推断出适合Lambda的签名,因为函数描述符可以通过目标类型来得到。这样做的好处在于,编译器可以了解Lambda表达式的参数类型,这样就可以在Lambda语法中省去标注参数类型。

List<Apple> greenApples = filter(inventory, a -> "green".equals(a.getColor()));

Lambda表达式有多个参数,代码可读性的好处就更为明显。例如,你可以这样来创建一个Comparator对象

Comparator<Apple> c = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());//有类型推断
Comparator<Apple> c = (a1, a2) -> a1.getWeight().compareTo(a2.getWeight());

使用局部变量

不鼓励使用外部变量

Lambda表达式也允许使用自由变量(不是参数,而是在外层作用域中定义的变量),就像匿名类一样。 它们被称作捕获Lambda

int portNumber = 1337;
Runnable r = () -> System.out.println(portNumber);
portNumber = 31337;

错误: Lambda表达式引用的局部变量必须是最终的(final
或事实上最终的


为什么局部变量有这些限制?

  1. 实例变量和局部变量背后的实现有一个关键不同。实例变量都存储在中,而局部变量则保存在上。如果Lambda可以直接访问局部变量,而且Lambda是在一个线程中使用的,则使用Lambda的线程,可能会在分配该变量的线程将这个变量收回之后,去访问该变量。因此,Java在访问自由局部变量时,实际上是在访问它的副本,而不是访问原始变量。如果局部变量仅仅赋值一次那就没有什么区别了——因此就有了这个限制。

  2. 这一限制不鼓励你使用改变外部变量的典型命令式编程模式。

方法引用

方法引用的基本思想是,如果一个Lambda代表的只是“直接调用这个方法”,那最好还是用名称来调用它,而不是去描述如何调用它。

事实上,方法引用就是让你根据已有的方法实现来创建
Lambda表达式。

但是,显式地指明方法的名称,你的代码的可读性会更好

PS. I don’t think so.

它是如何工作的呢?当你需要使用方法引用时,目标引用放在分隔符::前,方法的名称放在后面。例如,Apple::getWeight就是引用了Apple类中定义的方法getWeight。

先前:

inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));

之后(使用方法引用和java.util.Comparator.comparing):

inventory.sort(comparing(Apple::getWeight));

管中窥豹

Lambda及其等效方法引用的例子

Lambda 等效的方法引用
(Apple a) -> a.getWeight() Apple::getWeight
() -> Thread.currentThread().dumpStack() Thread.currentThread()::dumpStack
(str, i) -> str.substring(i) String::substring
(String s) -> System.out.println(s) System.out::println

你可以把方法引用看作针对仅仅涉及单一方法的Lambda的语法糖,因为你表达同样的事情时要写的代码更少了。

如何构建方法引用

方法引用主要有三类。

  1. 指向静态方法的方法引用(例如Integer的parseInt方法,写作Integer::parseInt)

  2. 指向任意类型实例方法的方法引用(例如String的length方法,写作String::length)。//(String s) -> s.toUppeCase()

  3. 指向现有对象的实例方法的方法引用(假设你有一个局部变量expensiveTransaction用于存放Transaction类型的对象,它支持实例方法getValue,那么你就可以写expensiveTransaction::getValue)//()->expensiveTransaction.getValue()

例子

第二种

List<String> str = Arrays.asList("a","b","A","B");
str.sort((s1, s2) -> s1.compareToIgnoreCase(s2));List<String> str = Arrays.asList("a","b","A","B");
str.sort(String::compareToIgnoreCase);

第二种

Function<String, Integer> stringToInteger =
(String s) -> Integer.parseInt(s);Function<String, Integer> stringToInteger = Integer::parseInt;

第二种

BiPredicate<List<String>, String> contains =
(list, element) -> list.contains(element);BiPredicate<List<String>, String> contains = List::contains;

构造函数引用

无参构造函数的

Supplier<Apple> c1 = Apple::new;
Apple a1 = c1.get();

等同

Supplier<Apple> c1 = () -> new Apple();
Apple a1 = c1.get();

若构造函数是Apple(Integer weight)

Function<Integer, Apple> c2 = Apple::new;
Apple a2 = c2.apply(110);

等同

Function<Integer, Apple> c2 = (weight) -> new Apple(weight);
Apple a2 = c2.apply(110);

若构造函数是Apple(String color, Integer weight)

BiFunction<String, Integer, Apple> c3 = Apple::new;
Apple c3 = c3.apply("green", 110);

等同

BiFunction<String, Integer, Apple> c3 = (color, weight) -> new Apple(color, weight);
Apple c3 = c3.apply("green", 110);

运用实例

List<Integer> weights = Arrays.asList(7, 3, 4, 10);
List<Apple> apples = map(weights, Apple::new);public static List<Apple> map(List<Integer> list,Function<Integer, Apple> f){List<Apple> result = new ArrayList<>();for(Integer e: list){result.add(f.apply(e));}return result;
}

static Map<String, Function<Integer, Fruit>> map = new HashMap<>();static {map.put("apple", Apple::new);map.put("orange", Orange::new);
// etc...
}public static Fruit giveMeFruit(String fruit, Integer weight){return map.get(fruit.toLowerCase()).apply(weight);
}

构造函数引用

你已经看到了如何将有零个、一个、两个参数的构造函数转变为构造函数引用。那要怎么样才能对具有三个参数的构造函数,比如Color(int, int, int),使用构造函数引用呢?

构造函数引用的语法是ClassName::new,那么在这个例子里面就是Color::new。但是你需要与构造函数引用的签名匹配的函数式接口。但是语言本身并没有提供这样的函数式接口,你可以自己创建一个:

public interface TriFunction<T, U, V, R>{R apply(T t, U u, V v);
}

现在你可以像下面这样使用构造函数引用了:

TriFunction<Integer, Integer, Integer, Color> colorFactory = Color::new;

Lambda和方法引用实战

以排序为例

inventory.sort(comparing(Apple::getWeight));

第1步:传递代码

void sort(Comparator<? super E> c)public class AppleComparator implements Comparator<Apple> {public int compare(Apple a1, Apple a2){return a1.getWeight().compareTo(a2.getWeight());}
}inventory.sort(new AppleComparator());

第2步:使用匿名类

inventory.sort(new Comparator<Apple>() {public int compare(Apple a1, Apple a2){return a1.getWeight().compareTo(a2.getWeight());}
});

第3步:使用Lambda表达式

inventory.sort((Apple a1, Apple a2)-> a1.getWeight().compareTo(a2.getWeight()));//进一步简化
inventory.sort((a1, a2) -> a1.getWeight().compareTo(a2.getWeight()));//现在你可以把代码再改得紧凑一点了:Comparator<Apple> c = Comparator.comparing((Apple a) -> a.getWeight());import static java.util.Comparator.comparing;
inventory.sort(comparing((a) -> a.getWeight()));

第4步:使用方法引用

inventory.sort(comparing(Apple::getWeight));

复合Lambda表达式的有用的方法

比较器复合

逆序

inventory.sort(comparing(Apple::getWeight).reversed());

比较器链

inventory.sort(comparing(Apple::getWeight).reversed().thenComparing(Apple::getCountry));
//两个苹果一样重时,进一步按国家排序

谓词复合

谓词接口包括三个方法: negate、 and和or,让你可以重用已有的Predicate来创建更复杂的谓词。

//产生现有Predicate对象redApple的非
Predicate<Apple> notRedApple = redApple.negate();Predicate<Apple> redAndHeavyApple = redApple.and(a -> a.getWeight() > 150);Predicate<Apple> redAndHeavyAppleOrGreen = redApple.and(a -> a.getWeight() > 150).or(a -> "green".equals(a.getColor()));

函数复合

最后,你还可以把Function接口所代表的Lambda表达式复合起来。 Function接口为此配了andThen和compose两个默认方法,它们都会返回Function的一个实例。

Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.andThen(g);
int result = h.apply(1);//x=1,结果是4Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.compose(g);
int result = h.apply(1);//x=1,结果是3


做文本转换 为例

public class Letter{public static String addHeader(String text){return "From Raoul, Mario and Alan: " + text;}public static String addFooter(String text){return text + " Kind regards";}public static String checkSpelling(String text){return text.replaceAll("labda", "lambda");}
}

函数复合

Function<String, String> addHeader = Letter::addHeader;
Function<String, String> transformationPipeline = addHeader.andThen(Letter::checkSpelling).andThen(Letter::addFooter);

数学中类似的思想

积分

在这个例子里,函数f是一条直线,因此你很容易通过梯形方法(画几个三角形)来算出面积:

1/2 × ((3 + 10) + (7 + 10)) × (7 – 3) = 60

运用Lambda表达式

integrate((double x) -> x + 10, 3, 7)public double integrate(DoubleFunction<Double> f, double a, double b) {return (f.apply(a) + f.apply(b)) * (b-a) / 2.0;
}

小结

  • Lambda表达式可以理解为一种匿名函数:它没有名称,但有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常的列表。
  • Lambda表达式让你可以简洁地传递代码。
  • 函数式接口就是仅仅声明了一个抽象方法的接口。
  • 只有在接受函数式接口的地方才可以使用Lambda表达式。
  • Lambda表达式允许你直接内联,为函数式接口的抽象方法提供实现,并且将整个表达式作为函数式接口的一个实例。
  • Java 8自带一些常用的函数式接口,放在java.util.function包里,包括Predicate<T>、Function<T,R>、Supplier<T>、Consumer<T>和BinaryOperator<T>,
  • 为了避免装箱操作,对Predicate<T>和Function<T, R>等通用函数式接口的原始类型特化:IntPredicate、IntToLongFunction等。
  • 环绕执行模式(即在方法所必需的代码中间,你需要执行点儿什么操作,比如资源分配和清理)可以配合Lambda提高灵活性和可重用性。
  • Lambda表达式所需要代表的类型称为目标类型。
  • 方法引用让你重复使用现有的方法实现并直接传递它们。
  • Comparator、Predicate和Function等函数式接口都有几个可以用来结合Lambda表达式的默认方法。

《Java8实战》笔记(03):Lambda表达式相关推荐

  1. Java学习笔记之 Lambda表达式

    Java Lambda表达式 Lambda表达式 1.引言 2.为什么 Java 需要 Lambda 表达式 3.Lambda 表达式的结构 3.1.Lambda 表达式:语法 4.在哪里使用lamb ...

  2. java8新特性: lambda表达式:直接获得某个list/array/对象里面的字段集合

    java8新特性: lambda表达式:直接获得某个list/array/对象里面的字段集合 比如,我有一张表: entity Category.java service CategoryServic ...

  3. 【java8新特性】——lambda表达式与函数式接口详解(一)

    一.简介 java8于2014年发布,相比于java7,java8新增了非常多的特性,如lambda表达式.函数式接口.方法引用.默认方法.新工具(编译工具).Stream API.Date Time ...

  4. java8新特性之lambda表达式--超级详细版本

    java8新特性之lambda表达式 1.什么是lambda表达式?为什么用它? 2. 为什么Java需要lambda表达式? 3. lambda表达式的语法 4.函数式接口 4.1 什么是函数式接口 ...

  5. Java8 新特性:Lambda 表达式、方法和构造器引用、Stream API、新时间与日期API、注解

    Java8新特性:Lambda 表达式.方法和构造器引用.Stream API.新时间与日期API.注解 1.Java8新特性 1.1.主要的新特性: 1.2.编程风格 2.Lambda 表达式 2. ...

  6. java8新特性之Lambda表达式入门

    一 什么是Lambda表达式 Lambda表达式:可以让你的代码更加的简洁.ambda无法单独出现,需要一个函数式接口来盛放,可以说lambda表达式方法体是函数式接口的实现,lambda实例化函数式 ...

  7. 【读书笔记】Lambda表达式

    Lambda表达式,是一个匿名函数,它可以包含表达式和语句,并且可用于创建委托或表达式目录树类型. Lambda表达式, 都是用"=>"运算符. 读作"goes t ...

  8. 五十八、Java8的新特性Lambda表达式

    @Author:Runsen @Date:2020/6/24 人生最重要的不是所站的位置,而是内心所朝的方向.只要我在每篇博文中写得自己体会,修炼身心:在每天的不断重复学习中,耐住寂寞,练就真功,不畏 ...

  9. 夯实Java基础(二十二)——Java8新特性之Lambda表达式

    1.前言 Java 8于14年发布到现在已经有5年时间了,经过时间的磨练,毫无疑问,Java 8是继Java 5(发布于2004年)之后的又一个非常最重要的版本.因为Java 8里面出现了非常多新的特 ...

  10. Java8函数式接口与Lambda表达式

    摘要 何为函数式接口? 什么是lambda表达式,lambda表达式的本质: 函数式接口与lambda表达式的联系:lambda是实现函数式接口的一个快捷方式,可以作为函数式接口的一个实例: 常用Ja ...

最新文章

  1. inline-block在ie6中的经典bug
  2. oracle中escape关键字用法
  3. 关于http://127.0.0.1:4723/wd/hub的解释
  4. 分布式文件系统HDFS 练习
  5. 词频统计-------------web版本
  6. mysql now str,数据库学习之MySQL (十)—— 日期和时间函数 NOW STR_TO_DATE DATE_FORMAT 流程控制函数 IF IFNULL CASE...
  7. C++11系列学习之五-------decltype
  8. java 线程 wait sleep_JAVA线程sleep和wait方法区别
  9. Django Web开发【2】Django入门
  10. [HTML] Prettify 代码高亮使用总结
  11. Android Kotlin关于新增本地数据库对象表字段问题
  12. VMware Workstation安装windows xp系统并创建虚拟软盘
  13. python+selenium+pycharm安装
  14. Apollo学习笔记(22)Dijkstra算法
  15. Oracle数据库之子程序和包(八)
  16. freeCodeCamp Slasher Flick
  17. 特来电、星星充、国家电网三足鼎立的充电桩遭巨头偷袭
  18. 一文彻底讲清Linux tty子系统架构及编程实例
  19. 新闻爬虫步骤python_抓取新闻网站:异步爬虫实现的流程和细节
  20. 关于火绒安装,出现版本过低问题的解决办法

热门文章

  1. linux 挂载32T文件系统,linux Centos下磁盘分区及文件系统创建与挂载(示例代码)
  2. ud分区删除工具_硬盘分区GPT分区怎么转MBR呢?硬盘分区GPT分区转MBR教程
  3. 神经网络与深度学习——TensorFlow2.0实战(笔记)(四)(python模块、包和库)
  4. 【转】DCMTK开源库类继承结构与DICOM3.0标准元素定义的对应关系图
  5. mysql slowlog中querytime分析_技术分享 | Slow Query Log 使用详解
  6. wince投屏苹果手机_怎么把手机上的导航映射到中控屏
  7. Spark一些组件的定义
  8. Python 数据分析三剑客之 Pandas(十):数据读写
  9. 给各位ACMer,OIer详细介绍一下Codeforces比赛
  10. 【CodeForces - 219D 】Choosing Capital for Treeland (树形dp)