Lambda表达式的类型
lambda表达式本身没有特定类型。
虽然参数的类型和数量是如此,以及返回值的类型可以传达某些类型的信息,但这些信息只会
- 约束它可以分配的类型。 Lambda在以下方式之一以以下方式分配给功能接口类型时,Lambda会收到类型:
- 直接分配给功能类型,例如mypredicate = s - > s.isempty()
- 将其传递为具有功能类型的参数,例如, Stream.Filter(S - > S.Isempty())
- 从返回函数类型的函数返回它,例如,返回s - > s.isempty()
在对函数类型进行任何此类赋值之前,lambda没有确定的类型。为了说明,考虑lambda表达式O-> O.ISUNTY()。相同的lambda表达式可以分配给许多不同的函数类型:
Predicate<String> javaStringPred = o -> o.isEmpty(); Function<String>, javaFunc = o -> o.isEmpty(); Predicate<List> javaListPred = o -> o.isEmpty(); Consumer<String> javaStringConsumer = o -> o.isEmpty(); //return value is ignored! com.google.common.base.Predicate guavaPredicate = o -> o.isEmpty();
既然分配了它们,那么所示的示例是完全不同的类型,即使lambda表达式看起来是相同的,它们也不能相互分配。
使用Lambda表达式对集合排序
排序列表
在Java 8之前,有必要在排序列表1时使用匿名(或者命名)类实现java.util.Comparator接口:
List people = … Collections.sort( people, new Comparator() { public int compare(Person p1, Person p2){ return p1.getFirstName().compareTo(p2.getFirstName()); } } );
版本≥javase 1.2
Collections.sort( people, (p1, p2) -> p1.getFirstName().compareTo(p2.getFirstName()) );
从Java 8开始,可以用Lambda表达式替换匿名类。
请注意,可以遗漏参数P1和P2的类型,因为编译器将自动推断出来:
Collections.sort( people, Comparator.comparing(Person::getFirstName) );
通过使用比较器,可以简化该示例.Comparing和方法引用使用::表示
import static java.util.Collections.sort; import static java.util.Comparator.comparing; //… sort(people, comparing(Person::getFirstName));
静态导入允许我们更简单地表达这一点,但这是值得易于改进的整体可读性:
sort(people, comparing(Person::getFirstName).thenComparing(Person::getLastName));
也可以将比较器建成的,也可以将这种方式连接在一起。
例如,在通过他们的名字比较人们之后,如果有人的名字,左上一名称的Thencomparing方法也比较:
注意集合.sort(...)仅适用于列表子类型的集合。
集合和集合API并不意味着元素的任何排序。
排序map
您可以用类似的方式按值对HashMap的条目进行排序(请注意,LinkedHashMap必须用作目标。普通HashMap中的键是无序的。)
Map map = new HashMap(); //… or any other Map class //populate the map map = map.entrySet() .stream() .sorted(Map.Entry.comparingByValue()) .collect(Collectors.toMap(k -> k.getKey(), v -> v.getValue(), (k, v) -> k, LinkedHashMap::new));
Java Lambdas简介
功能接口
Lambdas只能在功能接口上运行,这是一个只有一个抽象方法的接口。
功能接口可以具有任何数量的默认或者静态方法。
(因此,它们有时被称为单个抽象方法接口或者SAM接口)。
interface Foo1 { void bar(); } interface Foo2 { int bar(boolean baz); } interface Foo3 { String bar(Object baz, int mink); } interface Foo4 { default String bar() { //default so not counted return "baz"; } void quux(); }
在声明功能接口时,可以添加@FunctionalInterface注释。
这没有特殊效果,但如果该注释应用于不起作用的接口,则将生成编译器错误,从而提醒接口不应更改接口。
@FunctionalInterface interface Foo5 { void bar(); } @FunctionalInterface interface BlankFoo1 extends Foo3 { //inherits abstract method from Foo3 } @FunctionalInterface interface Foo6 { void bar(); boolean equals(Object obj); //overrides one of Object's method so not counted }
相反,这不是功能接口,因为它具有多个抽象方法:
interface BadFoo { void bar(); void quux(); //<-- Second method prevents lambda: which one should //be considered as lambda? }
这也不是功能接口,因为它没有任何方法:
interface BlankFoo2 { }
注意以下内容。
假设你有
interface Parent { public int parentMethod(); }
和
interface Child extends Parent { public int ChildMethod(); }
然后,由于它具有两种指定方法,因此不能成为功能接口。
Java 8还提供了包java.util.function中的许多通用模板功能接口。
例如,内置接口谓词软件包一个方法,该方法输入T型值并返回布尔值。
lambda表达式
Lambda表达的基本结构是:
然后将保存一个类的单例实例,类似于匿名类,它实现了functionalinterface,其中一个方法的定义是{system.out.println(“hello”); }。
其他
单词,以上大多等同于:
FunctionalInterface fi = new FunctionalInterface() { @Override public void theOneMethod() { System.out.println("Hello"); } };
λ只有“大多数等效”到匿名类,因为在一个lambda中,如此,super或者toString()的表达式的含义引用了分配所发生的类,而不是新创建的
目的。
我们无法在使用lambda时指定方法的名称 - 但我们不应该需要,因为功能接口必须只有一个抽象方法,因此Java覆盖了那个。
在Lambda的类型不确定的情况下,(例如,过载方法)我们可以将演员添加到Lambda,以告诉编译器应该是什么,如:
Object fooHolder = (Foo1) () -> System.out.println("Hello"); System.out.println(fooHolder instanceof Foo1); //returns true
如果功能接口的单个方法采用参数,则应在Lambda的括号之间出现本地正式名称。
没有必要声明参数或者返回的类型
取自接口(虽然如果我们想要声明参数类型并非错误)。
因此,这两个例子是等同的:
Foo2 longFoo = new Foo2() { @Override public int bar(boolean baz) { return baz ? 1 : 0; } }; Foo2 shortFoo = (x) -> { return x ? 1 : 0; };
如果函数只有一个参数,则可以省略周围参数周围的括号:
Foo2 np = x -> { return x ? 1 : 0; }; //okay Foo3 np2 = x, y -> x.toString() + y //not okay
隐含返回值
如果放置在Lambda中的代码是Java表达式而不是语句,则将其视为返回表达式值的方法。
因此,以下两个是等同的:
IntUnaryOperator addOneShort = (x) -> (x + 1); IntUnaryOperator addOneLong = (x) -> { return (x + 1); };
访问本地变量(值闭合)
由于Lambdas是匿名类的语法速记,因此它们遵循相同的规则来访问封闭范围中的局部变量;变量必须被视为最终且未在Lambda内修改。
IntUnaryOperator makeAdder(int amount) { return (x) -> (x + amount); //Legal even though amount will go out of scope //because amount is not modified } IntUnaryOperator makeAccumulator(int value) { return (x) -> { value += x; return value; }; //Will not compile }
如果有必要以这种方式缠绕变化变量,则应使用保持变量副本的常规对象。
阅读更多用Lambda表达式的Java Closes。
因为Lambda是一个接口的实现,所以没有完成任何特殊的东西来使方法接受一个lambda:任何采用功能接口的功能也可以接受一个lambda。
接受lambdas.
public void passMeALambda(Foo1 f) { f.bar(); } passMeALambda(() -> System.out.println("Lambda called"));
Lambda表达式提供了一种使用表达式实现单个方法接口的清晰简洁的方法。它们允许您减少必须创建和维护的代码量。虽然类似于匿名类,但它们本身没有类型信息。需要进行类型推断。
方法引用使用现有方法而不是表达式实现函数接口。它们也属于lambda家族。