关闭 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 类比较尴尬。

    上一篇返回首页 下一篇

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

    别人在看

    抖音安全与信任开放日:揭秘推荐算法,告别单一标签依赖

    ultraedit编辑器打开文件时,总是提示是否转换为DOS格式,如何关闭?

    Cornell大神Kleinberg的经典教材《算法设计》是最好入门的算法教材

    从 Microsoft 下载中心安装 Windows 7 SP1 和 Windows Server 2008 R2 SP1 之前要执行的步骤

    Llama 2基于UCloud UK8S的创新应用

    火山引擎DataTester:如何使用A/B测试优化全域营销效果

    腾讯云、移动云继阿里云降价后宣布大幅度降价

    字节跳动数据平台论文被ICDE2023国际顶会收录,将通过火山引擎开放相关成果

    这个话题被围观超10000次,火山引擎VeDI如此解答

    误删库怎么办?火山引擎DataLeap“3招”守护数据安全

    IT头条

    平替CUDA!摩尔线程发布MUSA 4性能分析工具

    00:43

    三起案件揭开侵犯个人信息犯罪的黑灰产业链

    13:59

    百度三年开放2.1万实习岗,全力培育AI领域未来领袖

    00:36

    工信部:一季度,电信业务总量同比增长7.7%,业务收入累计完成4469亿元

    23:42

    Gartner:2024年全球半导体营收6559亿美元,AI助力英伟达首登榜首

    18:04

    技术热点

    iOS 8 中如何集成 Touch ID 功能

    windows7系统中鼠标滑轮键(中键)的快捷应用

    MySQL数据库的23个特别注意的安全事项

    Kruskal 最小生成树算法

    Ubuntu 14.10上安装新的字体图文教程

    Ubuntu14更新后无法进入系统卡在光标界面解怎么办?

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

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