参考资料:

  《Java8 in Action》 Raoul-Gabriel Urma

一、jdk8

  客观的说,Java8是一次有重大演进的版本,甚至很多人认为java8所做的改变,在许多方面都比Java历史上任何一次改变都深远。

    Scala,python这样优秀编程语言中对函数式编程的处理,guava中的理念等等....

  java8的代码更简洁...

  java8对并发编程更加友好,java一直努力让并发编程更为高效,出错更少,jdk1.0里有线程和锁,Java 5增加的工业模块,Thread pools和一大堆并发工具类, Java7增加了fork/join框架,Java8则对并行提供了一个新的思路

  新的API,例如Stream等  

  等等

二、通过行为参数化传递代码

首先有一个苹果类

   public static class Apple {
private int weight = 0;
private String color = ""; public Apple(int weight, String color){
this.weight = weight;
this.color = color;
} public Integer getWeight() {
return weight;
} public void setWeight(Integer weight) {
this.weight = weight;
} public String getColor() {
return color;
} public void setColor(String color) {
this.color = color;
} public String toString() {
return "Apple{" +
"color='" + color + '\'' +
", weight=" + weight +
'}';
}
}

1. 需求

  需要2个函数以供过滤出需要的苹果

public static List<Apple> filterApplesByColor(List<Apple> inventory, String color){
List<Apple> result = new ArrayList<Apple>();
for(Apple apple: inventory){
if(apple.getColor().equals(color)){
result.add(apple);
}
}
return result;
} public static List<Apple> filterApplesByWeight(List<Apple> inventory, int weight){
List<Apple> result = new ArrayList<Apple>();
for(Apple apple: inventory){
if(apple.getWeight() > weight){
result.add(apple);
}
}
return result;
}

上面的代码比较啰嗦,而且如果还有新的需求,依旧需要添加新的方法,以前的java实现函数就是静态方法。

2. 第一次改造

Java中封装行为的方式只能通过匿名类之类的方式。首先定义一个接口:

interface ApplePredicate{
public boolean test(Apple a);
}

于是方法变为:

public static List<Apple> filter(List<Apple> inventory, ApplePredicate p){
List<Apple> result = new ArrayList<Apple>();
for(Apple apple : inventory){
if(p.test(apple)){
result.add(apple);
}
}
return result;
}

在之前的java,用如下方式:

static class AppleWeightPredicate implements ApplePredicate{
public boolean test(Apple apple){
return apple.getWeight() > 150;
}
}
static class AppleColorPredicate implements ApplePredicate{
public boolean test(Apple apple){
return "green".equals(apple.getColor());
}
}

2个新的实现类,从设计模式上是策略模式... 可以更灵活的扩展,但是感觉代码更啰嗦了...

当然你也可以不定义出类,使用匿名内部类,还是很啰嗦.

        List<Apple> redApples2 = filter(inventory, new ApplePredicate() {
public boolean test(Apple a){
return a.getColor().equals("red");
}
});

3. 使用Lambda表达式

List<Apple> result = filter(inventory ,(Apple apple)-> "red".equals(apple.getColor()));

jdk8中再进一步将Perdicate抽象为泛型类...

public interface Predicate<T> {
boolean test(T var1);
//....
}

三、Lambda表达式

3.1 lambda简介

很多语言都支持lambda,例如python,可以把lambda表达式简单理解为表示可传递的匿名函数的一种方式: 它没有名称,但是有参数列表、函数主题、返回类型,可能还有一个异常列表.

  • 匿名 - 没有方法名
  • 函数 - Lambda不属于任何一个类。
  • 传递 - 可以做为参数传递给方法或者存储在变量中
  • 简洁

让我们来改造一段代码...

        Comparator<Apple> byWeight = new Comparator<Apple>() {
@Override
public int compare(Apple o1, Apple o2) {
return o1.getWeight().compareTo(o2.getWeight());
}
}; Comparator<Apple> byWeight2 = (Apple a1, Apple a2) -> a1.getWeight() - a2.getWeight();

(Apple a1, Apple a2) 是Lambda的参数列表,加上一个小箭头,加上主体 a1.getWeight() - a2.getWeight()

Lambda的基本语法:

  (params) -> experssion

  (params) -> {statements;}

举例:

(1) () -> {};

(2) () -> "Raoul"

(3) () -> {return "aaa";}

(4) (int i) -> return "Al" + 1;

(5) (String s) -> {"IronMan";}

上述中只有(4), (5) 不符合语法,(4)的主体是statements;需要用{},(5)的主体是statements,因此不能使用{}...

3.2 在哪里可以使用Lambda

1. 函数式接口

  函数是接口就是只定义了一个抽象方法的接口。 jdk8中接口还可以定义默认方法,哪怕有很多的默认方法,但是只要接口只定义了一个抽象方法,它就仍然是一个函数式接口。

  Lambda允许你把lambda表达式作为函数式接口的一个实现的实例

  

     MyInteface myInteface = (int i, int j) -> i+j;
System.out.println(myInteface.add(1,2)); public interface MyInteface{
int add(int i, int j);
}

2. 函数式描述符

函数式接口中有且只有一个方法,这个方法的签名就是lambda表达式的签名。这种抽象方法叫做函数描述符...

@FuntionalInterface?

  接口标注,表示接口会被设计为一个函数式接口。按照官方说法就是:  

  • The type is an interface type and not an annotation type, enum, or class.
  • The annotated type satisfies the requirements of a functional interface.

3.3 使用函数式接口

java.util.function包中引入了常用的函数式接口

1. Predicate

    public static <T> List<T> filte(List<T> list, Predicate<T> p){
List<T> results = new ArrayList<T>();
for (T t : list) {
if (p.test(t)) results.add(t);
}
return results;
}

Predicate中还有一些add, not等默认方法,暂不讨论...

2. Consumer

其中定义了一个accept()方法,没有返回值

    public static <T> void forEach(List<T> list, Consumer<T> c){
for (T i : list) {
c.accept(i);
}
} public static void main(String[] args) {
forEach(
Arrays.asList(1,2,3,4,5),
(Integer i) -> {
System.out.println(i);
}
);
}

3. Function

类似于Guava的Function,定义了一个apply的方法,例子中打印每个字符串的长度...

    public static <T, R> List<R> map(List<T> list, Function<T, R> f){
List<R> result = new ArrayList<R>();
for (T t : list) {
result.add(f.apply(t));
}
return result;
} public static void main(String[] args) {
List<Integer> l = map(
Arrays.asList("lambdas","in","action"),
(String s) -> s.length()
);
System.out.println(l);
}

4. IntPredicate

自从jdk1.5之后就支持自动装箱inbox,但装箱之后比如将一个原始类型转换成了Integer,在heap划分一块内存分配等等,IntPredicate可以避免无谓的装箱..

IntPredicate evenNumbers = (int i) -> i%2 ==1;
Predicate<Integer> predicate = (Integer i) -> i%2 ==1;

也可以说因为泛型往往不能使用基本类型

3.4 方法引用

方法引用可以被看做仅仅调用特定Lambda的一种快捷写法。例如

(Apple a) -> a.getWeight() 可以写成 Apple::getWeight

() -> Thread.currentThread.dumpStack() 可以写成 Thread.currentThread()::dumpStack

(str, i) -> str.substring(i) 可以写成 String:substring

(Sring s) -> System.out.println(s) 可以写成 System.out::println

如何构建方法引用?

(1) 静态方法,类的方法,例如Interger的parseInt Interger::parseInt

(2) 实例方法, String::length

(3) 指向现有方法的方法引用: expensiveTransaction::getValue

实例: 排序

使用Lambda表达式的用法

    public static void main(String[] args) {
Comparator<String> comparator = (String s1,String s2) -> s1.compareToIgnoreCase(s2);
List<String> l = Arrays.asList("a","b","A","B");
Collections.sort(l,comparator);
System.out.println(l);
}

可换成:

Comparator<String> comparator = String::compareToIgnoreCase;
List<String> l = Arrays.asList("a","b","A","B");
Collections.sort(l,comparator);
System.out.println(l);

构造函数引用...

3.5 Lambda表达式复合用法

1. 比较器复合

public static void main(String ... args){
List<Apple> inventory = new ArrayList<>();
inventory.addAll(Arrays.asList(new Apple(80,"green"), new Apple(155, "green"), new Apple(120, "red")));
//1. 使用逆序
inventory.sort(
Comparator.comparing(Apple::getWeight).reversed()
);
//2. 比较器链
inventory.sort(
Comparator.comparing(Apple::getWeight).reversed().thenComparing(Apple::getColor)
);
}

比较器链中如果重量相同,就按颜色排序...

2. 谓词复合

    public static void main(String ... args){
Predicate<Apple> redApple = (Apple a) -> "red".equals(a.getColor());
Predicate<Apple> notRedApple = redApple.negate();
Predicate<Apple> redAndHeavyApple = redApple.and(a -> a.getWeight()>150);
Predicate<Apple> readAndHeavyOrGreenApple = redApple.and(a -> a.getWeight()>150).or(a -> "green".equals(a.getColor()));
}

3. 函数复合

以Function为例,实现类似于g(f(x))和f(g(x))的效果

    public static void main(String... args) {
Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.andThen(g);
Function<Integer, Integer> k = f.compose(g);
System.out.println(h.apply(3));
System.out.println(k.apply(3));
}

Java8 (1)的更多相关文章

  1. Java8实战分享

    虽然很多人已经使用了JDK8,看到不少代码,貌似大家对于Java语言or SDK的使用看起来还是停留在7甚至6. Java8在流式 or 链式处理,并发 or 并行方面增强了很多,函数式的风格使代码可 ...

  2. java8中lambda表达式的应用,以及一些泛型相关

    语法部分就不写了,我们直接抛出一个实际问题,看看java8的这些新特性究竟能给我们带来哪些便利 顺带用到一些泛型编程,一切都是为了简化代码 场景: 一个数据类,用于记录职工信息 public clas ...

  3. Android Studio2.1.2 Java8环境下引用Java Library编译出错

    转载请注明出处:http://www.cnblogs.com/LT5505/p/5685242.html 问题:在Android Studio2.1.2+Java8的环境下,引用Java Librar ...

  4. Java笔记——Java8特性之Lambda、方法引用和Streams

    Java8已经推出了好一段时间了,而掌握Java8的新特性也是必要的,如果要进行Spring开发,那么可以发现Spring的官网已经全部使用Java8来编写示例代码了,所以,不学就看不懂. 这里涉及三 ...

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

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

  6. Java8并发教程:Threads和Executors

    来之:ImportNew 欢迎阅读我的Java8并发教程的第一部分.这份指南将会以简单易懂的代码示例来教给你如何在Java8中进行并发编程.这是一系列教程中的第一部分.在接下来的15分钟,你将会学会如 ...

  7. java8 学习系列--NIO学习笔记

    近期有点时间,决定学习下java8相关的内容: 当然了不止java8中新增的功能点,整个JDK都需要自己研究的,不过这是个漫长的过程吧,以自己的惰性来看: 不过开发中不是有时候讲究模块化开发么,那么我 ...

  8. Java8函数式编程

    在Java8的 java.util.function中包含以下几个接口 1.Function,先上源码 /* * Copyright (c) 2010, 2013, Oracle and/or its ...

  9. Java8闭包

    闭包在很多语言中都存在,例如C++,C#.闭包允许我们创建函数指针,并把它们作为参数传递,Java编程语言提供了接口的概念,接口中可以定义抽象方法,接口定义了API,并希望用户或者供应商来实现这些方法 ...

  10. Java8新特性——接口的默认方法和类方法

    Java8新增了接口的默认方法和类方法: 以前,接口里的方法要求全部是抽象方法,java8以后允许在接口里定义默认方法和类方法: 不同的是: 默认方法可以通过实现接口的类实例化的对象来调用,而类方法只 ...

随机推荐

  1. Activiti工作流入门

    Activiti简介 Activiti是一个开源的工作流引擎,它实现了BPMN 2.0规范,可以发布设计好的流程定义,并通过api进行流程调度. Activiti 作为一个遵从 Apache 许可的工 ...

  2. 关于Mac中Clion使用OpenCV

    关于Mac中Clion使用OpenCV 目标 Clion能够使用OpenCV 步骤 下载安装cmake,官网下载 下载OpenCV mac/linux版 使用cmake gui编译opencv安装包, ...

  3. ionic for mac 新建与调试

    ionic官网:http://ionicframework.com/ 首先需要下载node.js,建议node管理方式请先详细查看林一篇博客http://www.cnblogs.com/minyc/p ...

  4. idea新建项目文件名为红色的解决办法

    Perference->version Control ->Directory添加项目路径,vcs选<none> 即可.

  5. linux服务器没网情况下手动安装软件几个方法

    1,找到一个有网的服务器,使用yumdownloader gcc,获取需要的rmp包: 2,在http://pkgs.org 下下载所需要的rpm包

  6. Android使用 startActivityForResult 、 onActivityResult 时的注意事项

    今天使用 startActivityForResult 时遇到两个问题,应该是常见问题了吧,浪费了些时间才搞定,做个记录. 1. onActivityResult 的触发顺序问题 这个问题很郁闷,我一 ...

  7. redis写shell与ssh免密码登陆

     redis-cli参数:-h :指定要连接的主机IP或域名-p :指定连接的端口-a :指定密码-r :执行指定的命令-n :数据库名-x :将最后一个参数输出为value redis写shell- ...

  8. vultr优惠码ssd vps赠送50美金,长期有效

    vultr最新优惠码.vultr vps注册教程,是大家关心的问题.网上流传很多vultr vps优惠码,鱼龙混杂,难以判断.其实,获取vultr优惠赠送美元的方式很简单. 第一种,新用户使用绑定信用 ...

  9. @classmethod及@staticmethod方法浅析【python】

    目前对于python中@classmethod 类方法和@staticmethod静态方法的有了一定的认识,之后有进一步的认识后继续记录. @classmethod :是和一个class类相关的方法, ...

  10. android自定义控件,其三个父类构造方法有什么区别

    android自定义控件时,通常需要重写父类构造函数.这三个够找函数具体啥时调用? public View (Context context) 是在java代码创建视图的时候被调用,如果是从xml填充 ...