lambda表达式需要函数式接口的支持 ==
所谓的函数式接口,是指只有一个抽象方法(这里注意不是只有一个方法,1.8以及后 interface中可以有默认实现的方法关键字是default,如果他有两个及以上抽象方法,就不是函数式接口)的接口
== jdk8帮我们提供了一个注解,帮助我们检查编译时是否合格
@FunctionInterface
default关键字在API中的解释为
意思是默认方法能够向库的接口添加新功能,并确保与为这些接口的旧版本编写的代码兼容。并且不用在其子类进行逐个实现。
default是在java8中引入的关键字,也可称为Virtual
extension methods——虚拟扩展方法。是指,在接口内部包含了一些默认的方法实现(也就是接口中可以包含实现的方法体,这打破了Java之前版本对接口的语法限制),从而使得接口在进行扩展的时候,不会破坏与接口相关的实现类代码。
1、调用父接口实现
package main.java.defaultt;
@FunctionalInterface
public interface Interface1 {
default void sayhello(String hello){
System.out.println(hello);
}
abstract void test1();
// abstract void test2();
}
package main.java.defaultt;
public class Test implements Interface1{
@Override
public void test1() {
}
public static void main(String[] args) {
Test test = new Test();
test.sayhello("ya");
}
}
输出为 ya 没有问题
2、同时继承两个接口 – 如果抽象方法同名则必须重新实现
否则报错
发生这种情况的原因是,实现类Test即实现了接口Interface1又实现了接口Interface2,恰巧两个接口中都定义可相同的默认方法。说白了就是编译器此时已经被干懵了,当我们在Test类中调用方法时,它不知道该去调用Interface1的默认方法还是去调用Interface2的方法。解决方法就是在实现类中实现该方法
我们都知道接口是可以有多实现的,如果在两个接口中都有了default修饰的相同方法,那么在实现类中程序将不知道该使用哪个default的方法了,这时就要显示声明使用哪一个
package main.java.defaultt;
package main.java.defaultt;
public interface Interface2 {
default void sayhello(String hello){
System.out.println("2:"+hello);
}
abstract void test1();
}
这样的实现是本类自己实现 类优先
package main.java.defaultt;
public class Test implements Interface1,Interface2{
/**这里有两种实现
* 1.本类重新实现
* @param hello
*/
@Override
public void sayhello(String hello) {
System.out.println("我自己的"+hello);
}
@Override
public void test1() {
}
public static void main(String[] args) {
Test test = new Test();
test.sayhello("ya");
}
}
这样的方式是指定父类的实现方法 充分证明了类优先于接口
package main.java.defaultt;
public class Test implements Interface1,Interface2{
/**这里有两种实现
* 2.指定父类的实现方法
* @param hello
*/
@Override
public void sayhello(String hello) {
Interface1.super.sayhello(hello);
Interface2.super.sayhello(hello);
}
@Override
public void test1() {
}
public static void main(String[] args) {
Test test = new Test();
test.sayhello("ya");
}
}
lambda表达式可以理解为一种匿名函数的的代替
lambda允许将函数作为一个方法的参数(函数作为方法的参数传递),将代码像数据一样传递,目的是简化代码的编写
Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更优雅的 Java 代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。
JDK 也提供了大量的内置函数式接口供我们使用,使得 Lambda 表达式的运用更加方便、高。
在这里重点关注的是函数式编程:即java中是将带有单个抽象方法的接口(或者,几乎都不是抽象类)作为函数类型。
创建函数对象的主要方式是通过匿名类。
函数式接口 变量名 = (参数1,参数2.....)->{
//方法体
}
package main.java.lambda;
public class Test {
public static void main(String[] args) {
//匿名内部类 -- 这种方式会生成匿名内部类文件
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("hello,this is old way");
}
};
new Thread(runnable).start();
//lambda表达式
Runnable runnable1 = ()->{System.out.println("hello,this is lambda way");};
new Thread(runnable1).start();
//lambda表达式简化 方法体只有一条语句时可以不写大括号 {}
Runnable runnable2 = ()->System.out.println("hello,这是简化后的lambda表达式");
new Thread(runnable2).start();
//其实可以最简化即在参数中去使用lambda或者创建匿名内部列
new Thread(()->System.out.println("这是最简化的写法")).start();
}
}
hello,this is old way
hello,this is lambda way
hello,这是简化后的lambda表达式
这是最简化的写法
Process finished with exit code 0
//比较器 传统模式 匿名类或者实现接口
Comparator<String> comparator = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length()-o2.length();
}
};
TreeSet<String> treeSet = new TreeSet<>(comparator);
//lambda 表达式
Comparator<String> comparator1 = (o1,o2)->o1.length()-o2.length();
TreeSet<String> treeSet1 = new TreeSet<>(comparator1);
TreeSet<String> treeSet2 = new TreeSet<>((o1,o2)->o1.length()-o2.length());
TreeSet<String> treeSet3 = new TreeSet<>((o1,o2)->{return o1.length()-o2.length();});
lambda引入了新的操作符: -> ,->将表达式分成两部分
左侧:(参数1,参数2.....)表示参数列表
右侧: { } 内部是方法体
1. 形参列表的数据类型会自动推断 ;
2. 如果形参列表为空,只需保留();
3. 如果形参只有一个,()可以省略,只需要参数名称即可;
4. 如果执行语句只有一句,且无返回值,{}可以省略,若有返回值,若想省去{},则必须省去return,且执行语句也保证只有一句;
5. lambda 表达式不会生成一个单独的内部类文件;
6. lambda表达式若访问了局部变量,则局部变量必须是final的,若是局部变量没有加final关键字,系统会自动添加,此后在修改该局部变量,汇报错;
Consumer是一个函数式编程接口; 顾名思义,Consumer的意思就是消费,即针对某个东西我们来使用它,
因此它包含有一个有输入而无输出的accept接口方法;
void accept(T t);
除accept方法,它还包含有andThen这个方法;
其定义如下:
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
测试
Consumer c = System.out::println;
Consumer c2 = n -> System.out.println(n + "-c2");
//执行完c后再执行c2的Accept方法
c.andThen(c2).accept("Consumer");
//连续执行c的Accept方法
c.andThen(c).andThen(c).andThen(c).accept("testFun");
Consumer
Consumer-c2
testFun
testFun
testFun
testFun
Function也是一个函数式编程接口;它代表的含义是“函数”,而函数经常是有输入输出的,因此它含有一个apply方法,包含一个输入与一个输出;
除apply方法外,它还有compose与andThen及indentity三个方法,其使用见下述示例;
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
测试
Function<String, String> f = s -> s+"f|";
Function<String, String> g = s -> s+"g|";
/**
* 下面表示在执行f时,先执行g,并且执行f时使用g的输出当作输入。
* 相当于以下代码:
* String a = g.apply("1");
* System.out.println(f.apply(a));
*/
System.out.println(f.compose(g).apply("1"));
/**
* 表示执行f的Apply后使用其返回的值当作输入再执行g的Apply;
* 相当于以下代码
* String a = f.apply("2");
* System.out.println(g.apply(a));
*/
System.out.println(f.andThen(g).apply("2"));
/**
* identity方法会返回一个不进行任何处理的Function,即输出与输入值相等;
*/
System.out.println(Function.identity().apply("a"));
1g|f|
2f|g|
a
Predicate为函数式接口,predicate的中文意思是“断定”,即判断的意思,判断某个东西是否满足某种条件; 因此它包含test方法,根据输入值来做逻辑判断,其结果为True或者False。
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate<T> negate() {
return (t) -> !test(t);
}
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
它的使用方法示例如下:
Predicate<String> p = o -> o.equals("test");
Predicate<String> g2 = o -> o.startsWith("t");
Assert.assertTrue(p.test("test"));
/**
* negate: 用于对原来的Predicate做取反处理;
* 如当调用p.test("test")为True时,调用p.negate().test("test")就会是False;
*/
Assert.assertFalse(p.negate().test("test"));
/**
* and: 针对同一输入值,多个Predicate均返回True时返回True,否则返回False;
*/
Assert.assertTrue(p.and(g2).test("test"));
/**
* or: 针对同一输入值,多个Predicate只要有一个返回True则返回True,否则返回False
*/
Assert.assertTrue(p.or(g2).test("ta"));
//对当前操作进行"="操作,即取等操作,可以理解为 A == B
Stream.of("String","TRE").filter(Predicate.isEqual("TRE")).forEach(System.out::println);
TRE
链接: 简单全面学习JDK1.8新特性之流式编程-SreamAPI(二).