匿名内部类与Lambda表达式示例

下面代码来源于:0027 Java学习笔记-面向对象-(非静态、静态、局部、匿名)内部类

package testpack;
public class Test1{
    public static void main(String[] args) {
        ProcessArray pa=new ProcessArray();
        int[] target={5,35,-2,35,-21};
        pa.process(target, new Command(){        //匿名内部类实现一个接口Command,不能传入参数
            public void process(int[] target){
                int sum=0;
                for (int tmp:target) {
                    sum+=tmp;
                }
                System.out.println("数组元素总和: "+sum);
            }
        });
    }
}
interface Command{
    void process(int[] target);
}
class ProcessArray{
    public void process(int[] target,Command cmd){
    cmd.process(target);
    }
}
  • 下面用Lambda表达式改写
package testpack;
public class Test1{
    public static void main(String[] args) {
        ProcessArray pa=new ProcessArray();
        int[] target={5,35,-2,35,-21};
        pa.process(target, (int[] array)->{   //改写从这开始。Lambda基本结构:()->{}
            int sum=0;
            for (int tmp:array){
                sum+=tmp;
            }
            System.out.println("数组元素总和: "+sum);
        });                                    //改写结束
    }
}
interface Command{
    void process(int[] target);
}
class ProcessArray{
    public void process(int[] target,Command cmd){
    cmd.process(target);
    }
}
  • Lambda表达式的基本结构

    • 形参列表:()圆括号部分

      • 形参列表位于圆括号中
      • 形参类型允许省略:上面代码中省略"int[]"也是可以的
      • 如果无参数,那就只写个()
      • 如果只有一个参数,可以省略圆括号
    • 箭头:->
    • 代码块:{}花括号部分
      • 代码块部分放在花括号中
      • 如果只有一条语句,那么可以省略花括号
      • Lambda只有一条return语句,可以省略return关键字,只有一条语句的话,就自动返回该语句的值
  • Lambda表达式示例:来源于《疯狂Java讲义第三部》.《LambdaQs.java》
package testpack;
public class Test1{
    public static void main(String[] args) {
        LambdaQs lq=new LambdaQs();
        lq.eat(()->System.out.println("苹果味道不错!"));   //实际上是创建了Eatable类型的匿名对象,重写了其taste()方法
        lq.drive(weather->{                               //实际上是创建了Flyable类型的匿名对象,重写了其fly(String weather)方法
            System.out.println("今天天气是: "+weather);
            System.out.println("直升机飞行平稳!");
        });
        lq.test((a,b)->a+b);                              //创建了Addable类型的对象,重写了add(int a,int b)方法
    }
}
interface Eatable{
    void taste();
}
interface Flyable{
    void fly(String weather);
}
interface Addable{
    int add(int a,int b);
}
class LambdaQs{
    public void eat(Eatable e){
        System.out.println(e);
        e.taste();
    }
    public void drive(Flyable f){
        System.out.println("我正在驾驶:"+f);
        f.fly("碧空如洗的晴日");
    }
    public void test(Addable add){
        System.out.println("5+3= "+add.add(5, 3));
    }
}

函数式接口与Lambda表达式

  • 函数式接口:只包含一个抽象方法的接口,可以包含多个默认方法、类方法,但只能包含一个抽象方法
  • 创建函数式接口的对象,可以通过匿名内部类和Lambda表达式
  • 函数式接口:java.lang.Runnable、java.awt.event.ActionListener
  • @FunctionalInterface 注解用于告知编译器执行更严格的检查,该接口必须是函数式接口,否则报错
  • Lambda表达式的结果就是一个对象,可以将其赋值给一个函数式接口类型的变量,只要二者的参数列表匹配,示例:
package testpack;
public class Test1{
    public static void main(String[] args) {
        Runnable r=()->{                            //Runnable是一个函数式接口,只有一个无参数的抽象方法
            System.out.println("lambda表达式的实现了一个无参的方法,可以赋值给Runnable类型变量");
        };
        r.run();
    }
}
  • Lambda表达式只能为函数式接口创建对象,即只包含一个抽象方法的接口,只能实现一个方法
  • Lambda表达式的目标类型必须是明确的函数式接口,但具体是哪个类型并不能确定,只要那个接口的抽象方法的参数列表跟Lambda表达式匹配即可,见示例
package testpack;
public class Test1{
    public static void main(String[] args) {
        Runnable r=()->System.out.println("同一个Lambda表达式可以赋值给Runnable和A,只要他们抽象方法的参数列表相匹配");
        r.run();
        A a=()->System.out.println("同一个Lambda表达式可以赋值给Runnable和A,只要他们抽象方法的参数列表相匹配");
        a.a();
    }
}
interface A{
    void a();
}
  • 确保正确的使用Lambda表达式

    • 将其赋值给一个函数式接口类型的变量,当然参数列表得匹配
    • 将其作为一个函数式接口类型的参数传给某个方法
    • 使用函数式接口进行强制类型转换
  • 函数式接口有很多,比如下面这些:
    • java.util.function中的函数式接口

      • ...Function:一般包含一个apply()方法,对参数进行处理,然后返回一个值
      • ...Consumer:包含一个accept()方法,与上面方法类似,只是不返回值
      • ...Predicate:包含test()方法,对参数进行判断,返回一个boolean值
      • ...Supplier:包含getAs...()方法,不需要参数,返回一个数据
      • ...Operator:
    • java.util.Comparator

Lambda表达式、方法引用、构造器引用

  • 如果Lambda表达式的代码块只有一条代码,则还有更加简洁的写法

    • 引用类方法:

      • 类名::类方法
      • 被实现方法的全部参数传给该方法作为参数
      • (a,b,...)->类名.类方法(a,b,...)
    • 引用特定对象的实例方法:
      • 特定对象::实例方法
      • 被实现方法的全部参数传给该方法作为参数
      • (a,b,...)->特定对象.实例方法(a,b,...)
    • 引用某类对象的实例方法:
      • 类名::实例方法
      • 被实现方法的第一个参数作为调用者,后面的参数全部传给该方法作为参数
      • (a,b,c,...)->a.实例方法(b,c,...)
    • 引用构造器:
      • 类名::new;
      • 被实现方法的全部参数传给该构造器作为参数
      • (a,b,...)->new 类名(a,b,...)
  • 见示例:
package testpack;

import javax.swing.JFrame;

public class Test1{
    public static void main(String[] args) { 

        Converter c1=(String from)->{return Integer.valueOf(from);};           //Lambda表达式
        Converter c2=Integer::valueOf;                                         //引用类方法
        System.out.println("c1: "+c1.convert("199")+"  c2: "+c2.convert("199"));

        Converter c3=(String from)->{return "ABCDEFGHIJKLMN".indexOf(from);}; //Lambda表达式
        Converter c4="ABCDEFGHIJKLMN"::indexOf;                               //引用特定对象的实例方法
        System.out.print(c3.convert("EFG"));
        System.out.println("     "+c3.convert("EFG"));

        Sub s1=(String str,int a,int b)->{return str.substring(a,b);};        //Lambda表达式
        Sub s2=String::substring;                                             //引用某类对象的实例方法
        System.out.println(s1.sub("ABCDEFGHIJKLMN", 3, 9));
        System.out.println(s1.sub("ABCDEFGHIJKLMN", 3, 9));

        Frame f1=(String title)->{return new JFrame(title);};                 //Lambda表达式
        JFrame jf1=f1.win("我的窗口");
        Frame f2=JFrame::new;                                                 //引用构造方法
        JFrame jf2=f2.win("我的窗口");
        System.out.println(jf1);
        System.out.println(jf2);
    }
}
interface Converter{
    Integer convert(String from);
}
interface Sub{
    String sub(String str,int a,int b);
}
interface Frame{
    JFrame win(String title);
}

Lambda表达式与匿名内部类

  • Lambda表达式可以在一定程度上看作是匿名内部类的子集,当匿名内部类实现的接口只有一个抽象方法(也就是函数式接口)时,可以用lambda
  • 相同之处:
    • 二者都可以直接访问外部类的实例变量和类变量,访问的局部变量都默认被final修饰
    • 二者创建的对象,都可以调用从接口中继承的默认方法
    • 见示例:
package testpack;
public class Test1{
    public static void main(String[] args) {
        LambdaAndInner li=new LambdaAndInner();
        li.test();
    }
}
class LambdaAndInner{
    private int age=13;
    private static String name="ABCDE";
    public void test(){
        String book="Java编程思想";
        Display dis=new Display(){      //匿名内部类
            public void display(){
                System.out.println("匿名内部类对象可以直接访问,局部变量book:"+book);
                System.out.println("匿名内部类对象可以直接访问,外部类的实例变量age:"+age);
                System.out.println("匿名内部类对象可以直接访问,外部类的类变量name:"+name);
                show();                 //在匿名内部类实现抽象方法的方法体内部调用接口默认方法
            }
        };
        dis.display();
        dis.show();                     //匿名内部类的对象调用接口的默认方法
    }
}
interface Display{
    void display();
    default void show(){
        System.out.println("这是接口的默认方法");
    }
}
package testpack;
public class Test1{
    public static void main(String[] args) {
        LambdaAndInner li=new LambdaAndInner();
        li.test();
    }
}
class LambdaAndInner{
    private int age=13;
    private static String name="ABCDE";
    public void test(){
        String book="Java编程思想";
        Display dis=()->{               //Lambda表达式
            System.out.println("Lambda表达式对象,可以直接访问局部变量book:"+book);
            System.out.println("Lambda表达式对象,可以直接访问外部类的实例变量age:"+age);
            System.out.println("Lambda表达式对象,可以直接访问外部类的类变量name:"+name);
            show();                     //Lambda表达式代码块内部调用接口默认方法,编译出错
        };
        dis.display();
        dis.show();                    //Lambda表达式的对象调用接口的默认方法
    }
}
interface Display{
    void display();
    default void show(){
        System.out.println("这是接口的默认方法");
    }
}
  • 区别:

    • 匿名内部类可以为任何接口创建实例,不管有几个抽象方法,只要都实现了就行;但Lambda只能为单抽象方法的接口创建实例
    • 匿名内部类除了可以为接口创建实例,还可以是抽象类普通类;但Lambda只能函数式接口
    • 匿名内部类实现抽象方法的方法体中可以调用默认方法,但Lambda代码块中不可以。见上面的示例代码

Lambda表达式的应用

  • 多用于返回一个某函数式接口的对象
  • 见示例:
package testpack;
import java.util.Arrays;
public class Test1{
    public static void main(String[] args) {
        String[] arr1=new String[]{"iOS","android","Java","C#","C","C++"};
        System.out.println(Arrays.toString(arr1));
        Arrays.parallelSort(arr1,(o1,o2)->o1.length()-o2.length());  //parallelSort(T[] a, Comparator<? super T> cmp)。Lambda的对象是Comparator类型
        System.out.println(Arrays.toString(arr1));

        int[] arr2=new int[]{2,6,-25,30,13,16};
        System.out.println(Arrays.toString(arr2));
        Arrays.parallelPrefix(arr2,(left,right)->left*right);   //  parallelPrefix(int[] array, IntBinaryOperator op)。Lambda的对象是IntBinaryOperator类型
        System.out.println(Arrays.toString(arr2));

        long[] arr3=new long[5];
        Arrays.parallelSetAll(arr3,operand->operand*5);  //parallelSetAll(long[] array, IntToLongFunction generator)。Lambda的对象是 IntToLongFunction类型
        System.out.println(Arrays.toString(arr3));

        //以上的Comparator、IntBinaryOperator、IntToLongFunction都是函数式接口,都有@FunctionalInterface注解
    }
}
  • 输出:

[iOS, android, Java, C#, C, C++][C, C#, iOS, C++, Java, android]
[2, 6, -25, 30, 13, 16][2, 12, -300, -9000, -117000, -1872000]
[0, 5, 10, 15, 20]

0028 Java学习笔记-面向对象-Lambda表达式的更多相关文章

  1. Java学习笔记--Java8 Lambda表达式

    Java Lambda表达式入门:http://blog.csdn.net/renfufei/article/details/24600507 lambda内容的介绍:http://swiftlet. ...

  2. 0030 Java学习笔记-面向对象-垃圾回收、(强、软、弱、虚)引用

    垃圾回收特点 垃圾:程序运行过程中,会为对象.数组等分配内存,运行过程中或结束后,这些对象可能就没用了,没有变量再指向它们,这时候,它们就成了垃圾,等着垃圾回收程序的回收再利用 Java的垃圾回收机制 ...

  3. 0025 Java学习笔记-面向对象-final修饰符、不可变类

    final关键字可以用于何处 修饰类:该类不可被继承 修饰变量:该变量一经初始化就不能被重新赋值,即使该值跟初始化的值相同或者指向同一个对象,也不可以 类变量: 实例变量: 形参: 注意可以修饰形参 ...

  4. 0013 Java学习笔记-面向对象-static、静态变量、静态方法、静态块、单例类

    static可以修饰哪些成员 成员变量---可以修饰 构造方法---不可以 方法---可以修饰 初始化块---可以修饰 内部类(包括接口.枚举)---可以修饰 总的来说:静态成员不能访问非静态成员 静 ...

  5. java学习笔记06--正则表达式

    java学习笔记06--正则表达式 正则表达式可以方便的对数据进行匹配,可以执行更加复杂的字符串验证.拆分.替换等操作. 例如:现在要去判断一个字符串是否由数字组成,则可以有以下的两种做法 不使用正则 ...

  6. 高放的c++学习笔记之lambda表达式

    lambda表达式:可以让代码看起来更整洁,有些结构简单且用的次数少的函数可以用lambda表达式替代, 通常结构是这样的[捕获列表](参数列表){函数部分} 捕获列表: lambda表达式如果在一个 ...

  7. 0029 Java学习笔记-面向对象-枚举类

    可以创建几个对象? n多个:大部分的类,都可以随意创建对象,只要内存不爆掉 1个:比如单例类 有限的几个:采用单例类的设计思路,可以只允许创建少数的几个特定的对象:还有就是枚举类. 创建少数几个对象, ...

  8. 0021 Java学习笔记-面向对象-包、构造器

    封装 面向对象的三大特征: 封装 继承 多态 封装: 将对象的状态信息隐藏,不允许外部程序直接访问 通过该类提供的方法来访问和操作 有啥用: 隐藏类的实现细节 在方法中加入控制逻辑,限制对成员变量的不 ...

  9. 0019 Java学习笔记-面向对象-方法

    方法属于谁 方法要么属于类,要么属于对象 static修饰的方法属于类 没有static修饰的方法属于对象 方法只能定义在类里面,不能独立定义 不能独立的执行方法,要么通过类调用,要么通过方法调用 一 ...

随机推荐

  1. Tree树节点选中及取消和指定节点的隐藏

    指定节点变色 指定节点隐藏 单击节点 未选中则选中该节点 已选中则取消该节点 前台: 1.HTML <ul id="listDept" name="listDept ...

  2. nyoj 106背包问题(贪心专题)

    背包问题 时间限制:3000 ms  |  内存限制:65535 KB 难度:3   描述 现在有很多物品(它们是可以分割的),我们知道它们每个物品的单位重量的价值v和重量w(1<=v,w< ...

  3. Odoo Graph 指定默认 类型

    <graph string='Sale Paid Grapg' type="pivot"> <field name='section_id' type=" ...

  4. 用python+selenium获取北上广深成五地PM2.5数据信息并按空气质量排序

    从http://www.pm25.com/shenzhen.html抓取北京,深圳,上海,广州,成都的pm2.5指数,并按照空气质量从优到差排序,保存在txt文档里 代码如下: #coding=utf ...

  5. CSS实现垂直居中的5种方法

    利用 CSS 来实现对象的垂直居中有许多不同的方法,比较难的是选择那个正确的方法.我下面说明一下我看到的好的方法和怎么来创建一个好的居中网站. 使用 CSS 实现垂直居中并不容易.有些方法在一些浏览器 ...

  6. Linux操作系统下搭建LAMP环境

    准备:先在目录home/csy/下建website代码目录,然后新建php文件,命名为test.php. 在test.php编写代码如下: <? php phpinfo(); ?> 保存并 ...

  7. 【转载】-- vi/vim使用

    vi/vim 基本使用方法本文介绍了vi (vim)的基本使用方法,但对于普通用户来说基本上够了!i/vim的区别简单点来说,它们都是多模式编辑器,不同的是vim 是vi的升级版本,它不仅兼容vi的所 ...

  8. CodeForces 559C Gerald and Giant Chess

    C. Gerald and Giant Chess time limit per test 2 seconds memory limit per test 256 megabytes input st ...

  9. 300. Longest Increasing Subsequence

    题目: Given an unsorted array of integers, find the length of longest increasing subsequence. For exam ...

  10. Git之使用

    一大清早的,正在熟睡的我竟然被冻醒了,这天好冷啊,没事就先来扯一下犊子吧:我发现我养成了一个“怪”习惯:我老是一大早起来打开电脑就撸代码,到了中午和下午就去干别的事不想敲了.我们寝室的宿友总是被我敲代 ...