Java 8 返回集合中第一个匹配的元素

在 Java 8 之前如果我们要找到集合中第一个匹配元素,要使用外部循环,如下面方法 findFirstMatch() 如果找到一个大于 3 的数字立即返回它,否则返回 null

  public Integer findFirstMatch() {
    List<Integer> integers = Arrays.asList(1, 4, 2, 5, 6, 3);
    for(int i: integers) {
      if(i > 3) return i;
    }
    return null;
  }

因为在 for 循环中找到第一个大于 3 的数字是 4, 并且立即返回,所以不管集合 integers 再大,也不会遍历整个集合。

注:不要纠结于上面示例方法的实际用途,实际上集体和匹配条件都该通过参数传入方法的,这里只作演示循环。

那么我们来到 Java 8 之后用 Stream API 该如何实现,翻遍了 Stream API, 能过滤元素的操作也就是 filter 方法,于是尝试这样的写法 阅读全文 >>

类别: Java/JEE. 标签: , . 阅读(47). 评论(0) »

Java 8 Lambda 捕获外部变量

可能会把捕获外部变量的 Lambda 表达式称为闭包,那么 Java 8 的 Lambda 可以捕获什么变量呢?

  1. 捕获实例或静态变量是没有限制的(可认为是通过 final 类型的局部变量 this 来引用前两者)
  2. 捕获的局部变量必须显式的声明为 final 或实际效果的的 final 类型

回顾一下我们在 Java 8 之前,匿名类中如果要访问局部变量的话,那个局部变量必须显式的声明为  final,例如下面的代码放在 Java 7 中是编译不过的

java7-annymous-capture-local-variable

Java 7 要求 version 这个局部变量必须是 final  类型的,否则在匿名类中不可引用。 阅读全文 >>

类别: Java/JEE. 标签: , . 阅读(116). 评论(0) »

Java 8 Lambda 写法与简化

Java 8 的 Lambda 表达式的实现方式还是基于已有的字节码指令,由 Lambda 表达式的方法签名结合上下文并经由 SAM 推断出正确的类型来。Java 8 的 Lambda 完整书写格式是

(type parameter1 [type parameter2, ...type parametern]) -> { statements here }

这种标准格式所表示的就是方法签名。

虽不及其他语言的 Lambda 表达式,像 Swift, Scala 可省略参数部分,可用默认参数名 $0, $1, 或 _, 但 Java 8 的 Lambda 还是可以进行酌情简化

  1. 参数类型总是可省略   --     (x, y) -> { x + y; }
  2. 参数为一个时,参数括号可省略  --    x -> { System.out.println(x); }
  3. 语句为一条时,可省略大括号, 并且语句后不要分号 --  x -> System.out.println(x)
  4. 上面更进一步,如果是单条 return 语句,必须把 return 关键字去掉  --  x -> "Hello " + x
  5. 就差一点,参数部分总是不能省,无参必须写成 () -> System.out.println("hi")
  6. Java 8 中若要近似的实现无参数部分写法,那就是方法引用了 -- System.out::println

阅读全文 >>

类别: Java/JEE. 标签: , . 阅读(91). 评论(0) »

JDK8 的 Lambda 表达式 -- 理解新式集合操作

有了前面的 SAM,Lambda 表达式,以及默认接口方法作铺垫后,我们可以去很好的去理解 Java8 用 Lambda  表达式操作集合的基本原理了。此篇我们想要化解的示例代码如下:

package cc.unmi;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author Unmi
 */
public class TestJava8Collection {
    public static void main(String[] args) {
        Collection collection = Arrays.asList("abc", "cde", "efg");
        List list = collection.stream().filter(x -> x.contains("c")).collect(Collectors.toList());
        list.forEach(x->System.out.println(x));
  }
}

如果对其他支持闭包的语言,如 JavaScript, Groovy, Ruby, Scala 等有所了解的话,很容易看出前面的代码输出为

abc
cde 阅读全文 >>

类别: Java/JEE. 标签: , , . 阅读(363). 评论(0) »

JDK8 的 Lambda 表达式 -- 默认和静态接口方法

进入 Java8 之后我们会发现接口可以有方法实现了,这与我们一直看待 Java 接口的观念产生了冲突,不过也别急,接口中的方法实现必须是一个默认方法,即像

interface Shape {
    default boolean isShape() {
        return true;
    }
}

本文旨在探讨 Java8 的默认接口方法存在的合理性,Java8 在这点上如何保持与前面版本的兼容性。

Lambda 和方法引用使得 Java 语言更具表现力。说到 Lambda 和方法引用的关系,Lambda 表达式的目的就是让你更为便捷的去绕过对象直接引用方法。

接口应该是相对稳固的,我们应该有这样的经验,类中使用了接口中定义的常量,如果在接口中改变了该常量值,单纯的替换接口对应的 class 文件是不奏效的,因为编译类时其实是把接口中的常量直接固化在类中了。如果类中要体现出最新常量值,那么使用接口的类也要重新编译。即使在接口中添加或改变了方法定义,也不能强制使用到它的类重新编译,早先的类完全可以自由的运行,因为接口中定义的常量和方法的所有内容都在自身,类一旦编译后便可脱离所实现的接口而运转。 阅读全文 >>

类别: Java/JEE. 标签: , , . 阅读(1,589). 评论(0) »

JDK8 的 Lambda 表达式 -- 方法引用

Lambda 允许我们定义匿名方法(即那个 Lambda 表达式,或叫闭包),作为一个功能性接口的实例。如果你不想把一个 Lambda 表达式写得过大,那么你可以把表达式的内容分离出来写在一个方法中,然后在放置 Lambda 表达式的位置上填上对那个方法的引用。

方法引用也应看作是一个 Lambda 表达式,所以它也需要一个明确的目标类型充当功能性接口的实例。简单说就是被引用的方法要与功能接口的 SAM(Single Abstract Method) 参数、返回类型相匹配。方法引用的引入避免了 Lambda 写复杂了可读性的问题,也使得逻辑更清晰。

为了应对方法引用这一概念, JDK8 又重新借用了 C++ 的那个 “::” 域操作符,全称为作用域解析操作符。

上面的表述也许不好明白,我看官方的那份 State of the Lambda 也觉得不怎么容易理解,特别是它举了那个例子很难让人望文生意。我用个自己写的例子来说明一下吧。

目前的 Eclipse-JDK8 版还不能支持方法引用的特性,幸好就是在昨天正式版的 NetBeans IDE 7.4 对 JDK8 有了较好的支持,所以在 NetBeans 7.4 中写测试代码。 阅读全文 >>

类别: Java/JEE. 标签: , . 阅读(518). 评论(0) »

JDK8 的 Lambda 表达式 -- 词法范围和变量捕获

Lambda 表达式的词法范围,一言以蔽之就是没有引入新的词法范围。这里的词法范围要研究的课题是 this 的指向有没有在变。我们知道在匿名类内部 this 指向的匿名类的实例,这种关系是在编译期就确定的。而在 Lambda 表达式中的 this 与外部的 this 没有差别,也就是说你可以把 Lambda 表达式当成一般的语句来看待,多简单啊,不像 JavaScript 中的 this 被搞的那么魔幻。

可以这么测试:

public void foo(){
    System.out.println(this);
    Arrays.asList("Unmi").forEach((s) -> System.out.println(this));
}

输出的是同一个地址里的东西:

cc.unmi.testjdk8.TestLambda@28a418fc
cc.unmi.testjdk8.TestLambda@28a418fc

接下来瞧瞧 Lambda 表达式对外层变量的捕获。Lambda 表达式的有个好处就是它是轻量级,可重用单元,并且可捕获外层变量。 阅读全文 >>

类别: Java/JEE. 标签: , . 阅读(196). 评论(0) »

JDK8 的 Lambda 表达式 -- 类型推断

现在我们来看看 JDK8 是怎么对 Lambda 表达式进行类型推断的,Lambda 的实际类型叫做它的目标类型(target type),因为 JDK8 沿承了已有的类型系统,所以象这样的写法:

button.addActionListener((ActionEvent e) -> foo());

addActionListener() 方法接收的是一个 ActionListener 类型的参数,所以这里的 Lambda 表达式 (ActionEvent e) -> foo() 代表的就是一个 ActionListener 实例,编译器是怎么知道这一点的,而且相同的 Lambda 是可以表示不同的类型的,见:

Callable<String> c = () -> "done";     //这个 () -> "done" 是 Callable<String> 类型
PrivilegedAction<String> a = () -> "done";  //同样的 () -> "done" 却是 PrivilegedAction<String> 类型

答曰,根据 Lambda 表达式所处的上下文去感知。上下文决定了 Lambda 所期盼的类型,比如说变量声明类型,方法要求的输入参数类型,所以它必须是明确,不能模棱两可。所以一个 Lambda 表达式能否放在某处需要满足以下几个条件:

  1. 期盼的类型必须是一个功能性接口,这样就能唯一定位到那个抽象方法上去,确定方法签名,接下来就是 阅读全文 >>
类别: Java/JEE. 标签: , . 阅读(359). 评论(3) »

JDK8 的 Lambda 表达式 -- 实现原理初探

JDK8 使用一行 Lambda 表达式可以代替先前用匿名类五六行代码所做的事情,那么它是怎么实现的呢?从所周知,匿名类会在编译的时候生成与宿主类带上 $1, $2 的类文件,如写在 TestLambda 中的匿名类产生成类文件是 TestLambda$1.class, TestLambda$2.class 等。

我试验了一下,如果使用的是 Lambda 表达式并不会生成额外的类文件,那么字节码里是什么样子的?来看下用  javap -c 反编译出下面文件产生的 TestLambda.class,两个方法,一个是  byAnonymousClass() 使用匿名类,另一个是 byLambda 使用 Lambda 的方式:

package cc.unmi.testjdk8;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;

public class TestLambda{
    private JButton button = new JButton();
    
    public void byLambda() {
        button.addActionListener((ActionEvent e) -> System.out.println("Lambda"));
    }
    
    public void byAnonymousClass(){
        
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("Anonymous class");
            }
        });
    }
}

阅读全文 >>

类别: Java/JEE. 标签: , . 阅读(801). 评论(0) »

JDK 8 的 Lambda 表达式 -- 写法

过去,我们使用匿名类的实现像  ActionListener 这样的接口,即使最简单的情景都需要写上五六行代码,这就显得有些累赘了。因为其实我们在 new ActionListener {} 时就是在创建一个 ActionListener 子类型,重载了方法,并初始化一个实例出来。现在我们手上有了 Lambda 表达式这一武器的话,就变得简单明了了,所以说  Lambda 是一种轻量级的实现机制。

这里是几个 Lambda 表达式的样子:

(int x, int y) -> x + y    //两整形参数,返回它们的和
() -> 42                   //无参数,直接返回 42
x -> 100;                  //可推断出参数 x 的类型
(String s) -> { System.out.println(s); } //传入一个字符,只执行一个操作,无返回值

Lambda 的格式就是三部分:参数列表,-> 分隔符,执行体。其他语方的 Lambda 格式都这样的,可能就是分隔符不一样,如 Ruby 的 |,Scala 的 => 等。

执行体里的 return 只是标明返回到匿名方法的调用者,不是返回值的意思; 最上层是不能用 break 和 continue 关键字的,可用在循环中; 如果 Lambda 表达式有返回值,那么在每一条路径上都要有返回值或是抛出异常。路径上最后一条语句的返回值即为 Lambda 表达的返回值。 阅读全文 >>

类别: Java/JEE. 标签: , . 阅读(319). 评论(0) »
Page 1 of 212