接口默认方法

  Java8版本以后新增了接口的默认方法,不仅仅只能包含抽象方法,接口也可以包含若干个实例方法、在接口内定义实例方法(但是注意需要使用default关键字)

在此定义的方法并非抽象方法,而是具有特定逻辑的实例方法。

  举例说明:定义接口Animal,其中包含默认方法eat().

/**
* Created by zjc on 2018/4/9.
*/
public interface Animal {
void call(); default void eat() {
System.out.println("Animal eat default method");
}
}

  定义一个Dog类实现接口Animal。

  

/**
* Created by zjc on 2018/4/9.
*/
public class Dog implements Animal {
@Override
public void call() {
System.out.println("wangwang");
}
}

  

  测试:

/**
* Created by zjc on 2018/4/9.
*/
public class AnimalTest {
public static void main(String[] args) {
Dog dog = new Dog() ;
dog.call();
dog.eat();
}
}

  输出结果:

  优势:

  1.JDK8默认方法允许接口方法定义默认实现,而所有子类都将拥有该方法及实现,不会破坏现有代码。有时我们需要在已经投入使用接口扩展一个新的方法,在JDK8之前我们必须在所有实现类中添加该方法的实现,否则会编译失败。如果改动的实现类少并且有权限修改时可能相对工作量还少,如果实现类改动的很多或者我们没有权限修改实现类的源代码,那就比较麻烦了,通过提供的这个默认方法则解决了这个问题,他提供了一个实现,当没有显示提供其他实现时就采用默认的这个实现,这样新添加的方法就不会破坏现有代码。

  2.默认方法可选:子类可以根据不同的需求Override默认实现。如果在接口中定义默认方法,则子类不需要必须实现该默认实现,如果有特殊需求或者需要,则可以Override该实现。

  当两个独立的默认方法相冲突或是默认方法和抽象方法相冲突时会产生编译错误。这时程序员需要显式覆盖超类方法。一般来说我们会定义一个默认方法,然后在其中显式选择超类方

  interface Robot implements Artist, Gun { default void draw() { Artist.super.draw(); } }

/**
* Created by zjc on 2018/4/9.
*/
public class Dog implements Animal {
@Override
public void call() {
System.out.println("wangwang");
} //子类可以根据不同的需求Override默认实现
@Override
public void eat() {
System.out.println("No default");
}
}

  

静态方法

在Java8的接口中,我们不光能写默认方法,还能写静态方法。

  

1.替代匿名内部类

毫无疑问,lambda表达式用得最多的场合就是替代匿名内部类,而实现Runnable接口是匿名内部类的经典例子。lambda表达式的功能相当强大,用()->就可以代替整个匿名内部类!请看代码:

如果使用匿名内部类:

    @Test
public void oldRunable() {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("The old runable now is using!");
}
}).start();
}

而如果使用lambda表达式:

    @Test
public void runable() {
new Thread(() -> System.out.println("It's a lambda function!")).start();
}

最后的输出:

The old runable now is using!
It's a lambda function!

是不是强大到可怕?是不是简单到可怕?是不是清晰明了重点突出到可怕?这就是lambda表达式的可怕之处,用极少的代码完成了之前一个类做的事情!

2.使用lambda表达式对集合进行迭代

Java的集合类是日常开发中经常用到的,甚至说没有哪个java代码中没有使用到集合类。。。而对集合类最常见的操作就是进行迭代遍历了。请看对比:

    @Test
public void iterTest() {
List<String> languages = Arrays.asList("java","scala","python");
//before java8
for(String each:languages) {
System.out.println(each);
}
//after java8
languages.forEach(x -> System.out.println(x));
languages.forEach(System.out::println);
}

如果熟悉scala的同学,肯定对forEach不陌生。它可以迭代集合中所有的对象,并且将lambda表达式带入其中。

languages.forEach(System.out::println);

这一行看起来有点像c++里面作用域解析的写法,在这里也是可以的。

3.用lambda表达式实现map

一提到函数式编程,一提到lambda表达式,怎么能不提map。。。没错,java8肯定也是支持的。请看示例代码:

    @Test
public void mapTest() {
List<Double> cost = Arrays.asList(10.0, 20.0,30.0);
cost.stream().map(x -> x + x*0.05).forEach(x -> System.out.println(x));
}

最后的输出结果:

10.5
21.0
31.5

map函数可以说是函数式编程里最重要的一个方法了。map的作用是将一个对象变换为另外一个。在我们的例子中,就是通过map方法将cost增加了0,05倍的大小然后输出。

4.用lambda表达式实现map与reduce

既然提到了map,又怎能不提到reduce。reduce与map一样,也是函数式编程里最重要的几个方法之一。。。map的作用是将一个对象变为另外一个,而reduce实现的则是将所有值合并为一个,请看:

    @Test
public void mapReduceTest() {
List<Double> cost = Arrays.asList(10.0, 20.0,30.0);
double allCost = cost.stream().map(x -> x+x*0.05).reduce((sum,x) -> sum + x).get();
System.out.println(allCost);
}

最终的结果为:

63.0

如果我们用for循环来做这件事情:

    @Test
public void sumTest() {
List<Double> cost = Arrays.asList(10.0, 20.0,30.0);
double sum = 0;
for(double each:cost) {
each += each * 0.05;
sum += each;
}
System.out.println(sum);
}

相信用map+reduce+lambda表达式的写法高出不止一个level。

5.filter操作

filter也是我们经常使用的一个操作。在操作集合的时候,经常需要从原始的集合中过滤掉一部分元素。

    @Test
public void filterTest() {
List<Double> cost = Arrays.asList(10.0, 20.0,30.0,40.0);
List<Double> filteredCost = cost.stream().filter(x -> x > 25.0).collect(Collectors.toList());
filteredCost.forEach(x -> System.out.println(x)); }

最后的结果:

30.0
40.0

将java写出了python或者scala的感觉有没有!是不是帅到爆!

6.与函数式接口Predicate配合

除了在语言层面支持函数式编程风格,Java 8也添加了一个包,叫做 java.util.function。它包含了很多类,用来支持Java的函数式编程。其中一个便是Predicate,使用 java.util.function.Predicate 函数式接口以及lambda表达式,可以向API方法添加逻辑,用更少的代码支持更多的动态行为。Predicate接口非常适用于做过滤。

    public static void filterTest(List<String> languages, Predicate<String> condition) {
languages.stream().filter(x -> condition.test(x)).forEach(x -> System.out.println(x + " "));
} public static void main(String[] args) {
List<String> languages = Arrays.asList("Java","Python","scala","Shell","R");
System.out.println("Language starts with J: ");
filterTest(languages,x -> x.startsWith("J"));
System.out.println("\nLanguage ends with a: ");
filterTest(languages,x -> x.endsWith("a"));
System.out.println("\nAll languages: ");
filterTest(languages,x -> true);
System.out.println("\nNo languages: ");
filterTest(languages,x -> false);
System.out.println("\nLanguage length bigger three: ");
filterTest(languages,x -> x.length() > 4);
}

最后的输出结果:

Language starts with J:
Java Language ends with a:
Java
scala All languages:
Java
Python
scala
Shell
R No languages: Language length bigger three:
Python
scala
Shell

  

漫漫人生路,学点Jakarta基础-Java8函数式编程的更多相关文章

  1. 漫漫人生路-学点Jakarta基础-Java8新特性 Stream/Lambda

    背景 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利.高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk dat ...

  2. Java8 函数式编程详解

    Java8 函数式编程详解 Author:Dorae Date:2017年11月1日23:03:26 转载请注明出处 说起Java8,可能很多人都已经知道其最大的改进,就是引入了Lambda表达式与S ...

  3. Java8函数式编程探秘

    引子 将行为作为数据传递 怎样在一行代码里同时计算一个列表的和.最大值.最小值.平均值.元素个数.奇偶分组.指数.排序呢? 答案是思维反转!将行为作为数据传递. 文艺青年的代码如下所示: public ...

  4. [2017.02.23] Java8 函数式编程

    以前学过Haskell,前几天又复习了其中的部分内容. 函数式编程与命令式编程有着不一样的地方,函数式编程中函数是第一等公民,通过使用少量的几个数据结构如list.map.set,以及在这些数据结构上 ...

  5. 关于Java8函数式编程你需要了解的几点

    函数式编程与面向对象的设计方法在思路和手段上都各有千秋,在这里,我将简要介绍一下函数式编程与面向对象相比的一些特点和差异. 函数作为一等公民 在理解函数作为一等公民这句话时,让我们先来看一下一种非常常 ...

  6. Python基础:函数式编程

    一.概述 Python是一门多范式的编程语言,它同时支持过程式.面向对象和函数式的编程范式.因此,在Python中提供了很多符合 函数式编程 风格的特性和工具. 以下是对 Python中的函数式编程 ...

  7. [一] java8 函数式编程入门 什么是函数式编程 函数接口概念 流和收集器基本概念

      本文是针对于java8引入函数式编程概念以及stream流相关的一些简单介绍 什么是函数式编程?   java程序员第一反应可能会理解成类的成员方法一类的东西 此处并不是这个含义,更接近是数学上的 ...

  8. python基础之函数式编程

    一.定义: 函数作为参数作用:将核心逻辑传入方法体,使该方法的适用性更广,体现了面向对象的开闭原则: 函数作为返回值作用:逻辑连续,当内部函数被调用时,不脱离当前的逻辑. 二.高阶函数: 1.定义:将 ...

  9. Java8函数式编程和lambda表达式

    文章目录函数式编程JDK8接口新特性函数接口方法引用函数式编程函数式编程更多时候是一种编程的思维方式,是一种方法论.函数式与命令式编程区别主要在于:函数式编程是告诉代码你要做什么,而命令式编程则是告诉 ...

随机推荐

  1. jquery的append/prepend和after/before有什么区别呢?

    append <p> <span class="s1">s1</span> </p> <script> $(" ...

  2. AngularJs轻松入门(八)Cookies读写

    虽然使用JavaScript创建和获取Cookie很简单,AngularJs还是把它作为一个单独的模块进行了封装,模块名为ngCookies,和前面的教程中做法一样,先引入angular-cookie ...

  3. ByteArray的操作总结(复制、打印、位运算)

    1. 字节数组的复制 Method A:Array.Clone() Clone返回的是Object对象,需要强类型转换:Clone会创建一个新的对象,并将value赋给dec ]; ]; dst = ...

  4. dedecms4张关键表解析之2

    4张核心表的具体情况: 1.第一张表:dede_arctype  栏目表 字段解析: topid:上一级的id(0表示为顶级,1表示为下一级....) typename: 栏目名称 typedir:栏 ...

  5. JVM监控工具介绍jstack, jconsole, jinfo, jmap, jdb, jstat(复制)

    jstack -- 如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程 ...

  6. DedeCMS让channelartlist支持currentstyle属性

    dedecms默认模板的channelartlist是不支持currentstyle属性的.currentstyle属性在导航中应用的比较多,可以实现循环调用栏目后,当前页<li>标签获得 ...

  7. Python中的引用计数法

    目录 引用计数法 增量操作 计数器溢出的问题 减量操作 终结器 插入计数处理 引用计数法 增量操作 如果对象的引用数量增加,就在该对象的计数器上进行增量操作.在实际中它是由宏Py_INCREF() 执 ...

  8. cal---显示日历

    cal命令用于显示当前日历,或者指定日期的日历. 语法 cal(选项)(参数) 选项 -l:显示单月输出: -3:显示临近三个月的日历: -s:将星期日作为月的第一天: -m:将星期一作为月的第一天: ...

  9. Git 服务器更换了IP的解决方法

    1.找到项目根目录中的.git文件夹 2..git文件夹里有一个config文件 3.用记事本打开后,修改为服务器的新ip就行了.

  10. codevs——T1337 银行里的迷宫

     时间限制: 1 s  空间限制: 128000 KB  题目等级 : 白银 Silver 题解       题目描述 Description 楚楚每一次都在你的帮助下过了一关又一关(比如他开宴会). ...