关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » JAVA »Java 8 Lambda 表达式学习心得整理

    Java 8 Lambda 表达式学习心得整理

    2015-11-22 00:00:00 出处:GreenDay
    分享

    lambda表达式,是一段可以传递的代码,可以被多次执行。在 java8 之前,如果我们想写一个简单的比较器 Compartor ,我们需要创建一个实现类或者一个匿名内部类类传入到需要比较的方法内当中。

    在 java8 之前传递一段代码不是很容易,现在我们想要实现一个通过传递代码来检查某个字符串的长度是否小于另外一个字符串的长度。

    (String first, String second) -> Integer.compare(first.length(), second.length());

    上面这段代码就是 lambda 表达式,这个表达式不仅仅是一个简单的代码块,还必须指定传递给代码的所有变量。

    Java 当中 lambda 表达式的格式是:参数、箭头(->)、以及一个表达式。如果负责计算的代码无法用一个表达式表示,可以使用 {} 括起来。

    如果 lambda 没有参数,可以使用 () 来表示,如果 lambda 表达式的参数类型可以被推导,那么可以省略掉。

    Comparator<String> comparator = (first, second) -> Integer.compare( first.length(), second.length());

    上面的例子当中会推导出 first 和 second 的类型是 String ,因为表达式赋值给了一个字符串比较器。

    注意,在 lambda 表达式当中只在某些分支有返回值是不合法的。

    函数式接口

    Java 当中有许多接口都需要封装代码块, Runnable 、 Compartor 等等。

    对于只包含一个方法的接口,可以通过 lambda 表达式来创建该接口的对象,这种接口被称为函数式接口。

    在 java.util.function 包下面提供了许多通用的函数式接口。

    可以在任意函数式接口上面使用 @FunctionalInterface 来标识它是一个函数式接口,但是该注解不是强制的。

    当 lambda 表达式被转换成一个函数式接口的实例时,需要注意处理检查时异常,如下代码。

    Runnable runnable = () -> {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };

    如果不加 try catch 语句的话,这个赋值语句就会编译错误,因为 Runnable 的 run 方法是没有异常抛出的。

    Callable 是可以抛出任何异常,并且有返回值,但是我们不想返回任何数据的时候可以如下定义:

    Callable<Void> callable = () -> {
                System.out.println("xxx");
                return null;
            };

    方法引用

    有时候,我们想传递的代码已经有现成的实现了。例如,我们仅仅想点击按钮时候打印 event 对象,可以进行如下代码:

    button.setOnAction(System.out::println);

    表达式 System.out::println 是一个方法引用,等同于 lambda 表达式 x -> System.out.println(x);

    :: 操作符将方法名和对象或类的名字分隔开。有以下三种主要的使用情况:

    对象 :: 实例方法 类 :: 静态方法 类 :: 实例方法

    前两种情况,方法引用相当于提供方法参数的 lambda 表达式。 System.out::println 等同于 x -> System.out.println(x);

    Math::pow 等同于 (x, y) -> Math.pow(x, y);

    第三种情况,第一个参数会成为执行方法的对象。例如, Sting::compareToIgnoreCase 等同于 (x, y) -> x.compareToIgnoreCase(y);

    Comparator<String> comparator = String::compareTo;

    当然还可以捕获 this 指针,this :: equals 相当于 x -> this.equals(x);

    构造器引用

    构造器引用与方法引类似,不同的是构造器引用使用的方法名是 new。例如,Buttton::new。

    List<String> strings = new ArrayList<String>();
    strings.add("a");
    strings.add("b");
    strings.add("c");
    Stream<Button> stream = strings.stream().map(Button::new);
    List<Button> buttons = stream.collect(Collectors.toList());

    先不详细介绍 stream map collect 方法,主要看对于每个列表元素会调用 Button 的构造方法。虽然 Button 有多个构造器,但是会选择只有一个 String 参数的构造器。

    数组类型的构造器引用,int[]::new 是一个含有一个参数的构造器引用,这个参数就是数组的长度,相当于 x -> new int[x]。

    变量作用域

    有如下代码:

    public static void repeat(String string, int count) {
            Runnable runnable = () -> {
                for (int i = 0; i < count; i++) {
                    System.out.println(this.toString());
                    Thread.yield();
                }
            };
            new Thread(runnable).start();
        }

    上面这段代码的两个参数没有设置成 final 的,这在 JDK7 之前是会编译错误的,同样在 java8 当中匿名内部类访问外部也不需要 final 来修饰。

    分析下上面的代码,由于有 Thread.yield 所以可能其他线程占用 CPU 先执行,然后方法 repeat() 先反回了,才执行 runnable,那么这个时候 string 和 count 这 2 个参数怎么办?

    首先一个 lambda 表达式需要有三个部分:

    一段代码 参数 自由变量的值,这里的“自由”指的是那些不是传入表达式的参数并且没有在代码中定义的变量。

    上面的那个例子当中有两个自由变量,string 和 count,lambad 表达式必须存放这两个变量的值。并且含有自由变量的代码块被称为闭包。

    在 lambda 表达式当中被引用的变量的值不可以被更改,编译器会检查修改操作:

    public void repeat(String string, int count) {
            Runnable runnable = () -> {
                for (int i = 0; i < count; i++) {
                    string = string + "a";//编译出错
                    System.out.println(this.toString());
                }
            };
            new Thread(runnable).start();
        }

    在 lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量。

    String first = "";
    Comparator<String> comparator = (first, second) -> Integer.compare(first.length(),//编译会出错
                    second.length());

    lambda 表达式中使用 this 会引用创建该 lambda 表达式的方法的 this 参数,

    public class Testmain2 {
        public static void main(String[] args) {
            Testmain2 testmain2 = new Testmain2();
            testmain2.method();
        }
    
        @Override
        public String toString() {
            return "aaaa";
        }
    
        public void method() {
            Runnable runnable = () -> {
                System.out.println(this.toString());
            };
            new Thread(runnable).start();
        }
    }

    上面的例子执行后会输出:aaaa。

    默认方法

    在集合库当中提供了一些函数表达式,例如 forEach 方法:

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

    由于集合的接口是之前定义的,新添加一个 forEach 方法会导致老的代码不兼容,但是 java8 当中是给接口设计成可以包含具体实现的默认方法来解决这个问题。

    public interface Person {
        long getID();
    
        default String getName() {
            return "name";
        }
    }

    如果要实现 Person 接口,那么必须实现 getID 方法,getName 方法可以不实现。

    如果一个接口定义了一个默认方法,而另外一个父类中又定义了同名的方法,那么如何选择?有以下规则:

    选择父类中的方法,如果父类提供了具体的实现方式,那么接口中具有相同名称和参数的默认方法会被忽略。 接口冲突,如果需要实现两个接口,并且这两个接口有两个相同签名的默认方法,那么子类就需要覆盖重写这个方法。

    如果一个子类继承了一个父类,并且实现了一个接口,并且父类和接口有相同签名的默认方法,那么之类继承父类当中的实现,类优先可以保持 java7 的兼容性。

    注意,不能为 Object 中的方法重新定义个默认方法。

    接口中的静态方法

    java8 可以在接口当中添加静态方法,便于把一些工具方法加入到接口当中,所以类似一些, Collections 和 Paths 类比较尴尬。

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    Edge浏览器百度被劫持/篡改怎么办,地址后边跟着尾巴#tn=68018901_7_oem_dg

    Google Chrome 在 iPhone 上新增了 Safari 数据导入选项

    Windows 11专业版 KMS工具激活产品密钥的方法

    DEDECMS安全策略官方出品

    Microsoft Text Input Application 可以关闭吗?

    新版本QQ如何关闭自带的浏览器?

    C++编程语言中continue的用法和功能,附举例示范代码

    c++ map 的数据结构、基本操作以及其在实际应用中的使用。

    C语言如何避免内存泄漏、缓冲区溢出、空指针解引用等常见的安全问题

    C语言中的break语句详解

    IT头条

    马斯克2026最新采访总结:2040年,全球机器人数量将突破100亿台

    23:52

    专家解读|规范人工智能前沿业态健康发展的新探索:解读《人工智能拟人化互动服务管理暂行办法》

    00:54

    用至强 6高存力搞定MoE卸载!

    17:53

    美国将允许英伟达向中国“经批准的客户”出售H200 GPU

    02:08

    苹果与微信就15%手续费达成一致?腾讯未置可否

    22:00

    技术热点

    PHP 和 Node.js 的10项对比挑战

    Javascript闭包深入解析及实现方法

    windows 7、windows 8.1手动增加右键菜单功能技巧

    MYSQL出错代码大汇总

    windows 7假死机怎么办 windows 7系统假死机的原因以及解决方法

    Ubuntu(Linux)下配置IP地址的方法

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。