java基础之Lamdba表达式01_表达式基础

Java8提供了Lamdba表达式,通过表达式可以简化一些代码的表达方式,设计思路和javascript的函数类似,此处我不希望花太多的时间来介绍Lambda的作用,对于Lamdba的学习,应该首先看看它是怎么一回事,学会之后通过几个实例即可掌握Lamdba表达式的作用和意义。

Lamdba表达式的表示方式

在java中我们通常都是定义单行变量,就好比如下的操作

int n = 10;
String str = "hello";
boolean b = true;

而Lamdba是函数变量,什么是函数变量呢,就等于我们用一个名称来表示一个函数,如下所示

helloFunction = public void sayHello() {
  System.out.println("hello world!");
}

对于上面一个函数变量而言,public是访问控制修饰符,在对于函数的变量而言,没有意义,可以省略,之后就成如下的样子

helloFunction = void sayHello(){
  System.out.println("hello world!");
}

对于函数变量而言,我们关心的是函数这个变量的结果,函数的名称就没有任何意义,void表示没有返回值,也没有意义,将这两个删除之后

helloFunction = () {
  System.out.println("hello world!");
}

如果这个函数中只有一行代码,我们可以将花括号省略,使用->来替换前花括号,最后就变成了如下的一个样子

helloFunction = ()->System.out.println("hello world!");

已上就是一个Lamdba表达式的写法,通过这个例子各位应该清楚什么是Lamdba了吧,它其实就是一个函数级别的变量,当然这个代码是无法被编译。在继续讲解之前,我们来看一下其他几个函数的转换问题

public int add(int a,int b) {
  return a+b;
}

对于以上函数,首先定义一个变量来存储这个函数,然后去掉public和add名称,最后就是返回值的处理,只要直接声明函数的结果是a+b即可

addFunction = (int a,int b)->a+b;

通过java实现Lamdba

下面我们会通过一个例子来实现一个Lamdba表达式,首先看helloFunction,对于helloFunction这个变量而言如果需要符合语法规则,第一是需要一个变量类型,在java8中通过接口来定义这个函数类型,只要创建一个接口,接口中有一个和这个函数的参数和返回值一样的抽象函数即可。

interface HelloFunction {
    /**
     * helloFunction中的函数没有返回值和没有参数,此时只要HelloFunction接口中
     * 有一个不带参数和没有返回值的函数即可
     */
    public void foo();
}

此时的helloFunction就可以使用这个接口作为变量类型

public class HelloLambda {
    public static void main(String[] args) {
        //HelloFunction中要存在一个同样类型和同样返回值的函数
        HelloFunction helloFunction = () -> System.out.println("hello world!");
        helloFunction.foo();
    }
}

此时如果修改HelloFunction中的函数的参数类型和返回值都会报错,而且HelloFunction中也不能添加其他的方法,如果添加其他方法都会报错,所以函数接口中只能有一个方法。Java提供了一个Annotation来声明这种函数接口@FunctionalInterface接下来看看add方法的操作,原理也一样,定义接口,接口中有个方法有两个int的参数和int的返回值。

@FunctionalInterface
interface AddFunction {
    public int add(int a,int b);
}

public static void main(String[] args) {
  //此时由于确定了函数值的类型,也就等于确定了参数的类型,所以,函数的参数的类型也可以省略
  AddFunction addFunction = (a,b) -> a+b;
  System.out.println(addFunction.add(12,22));
}

需要注意的是由于在定义函数变量时,可以通过函数变量的类型就可以知道里面的函数参数类型,所以在Lamdba表达式中也不用声明函数类型。

Lamdba表达式和匿名内部类

Lamdba表达式的这种方式非常类似Java的匿名内部类

//匿名内部类,整个操作和Lamdba非常类似
AddFunction af = new AddFunction() {
  public int add(int a, int b) {
    return a+b;
  };
};
System.out.println(af.add(12, 22));

两者的操作非常类似,当然到后面我们会发现他们之间的区别,目前我们所看到的主要区别有这样三点:1、匿名内部类的接口可以有多个方法,但是Lamdba的函数接口只能有一个方法。2、匿名内部类的实现方式比较麻烦一些。3、对于Lamdba表达式而言,并不一定是固定类型的,只要有另外一个接口满足这个函数的基本要求,就可以用来设置Lamdba表达式,这是Lamdba表达式的优点,利用这个优点可以快速的开发多线程的程序。举个例子,java的Runnable接口中只有一个run方法,这个接口和方法完全满足HelloFunction的需求,我们就可以按照下面的方式来定义这种写法。

Runnable rfunction = () -> System.out.println("hello world!");

此时的cfunction其实就是一个实现了Runnable的函数对象,同样可以作为Thread的参数传进去,就可以实现多线程的开发。

Lamdba作为函数的参数值

Lamdba表达式还可以作为函数的参数值来使用,看如下一个例子

public class TypeLamdba {

    public static void main(String[] args) {
//      StrLengthFunction sf = (s)->s.length();
//      printStrLen(sf, "this is a Lamdba!");
        //上面的两行代码可以使用下面这种表示方式
        printStrLen((s)->s.length(), "this is a Lamdba!");
    }
    
    /**
     * 该方法的参数使用了函数接口作为第一个参数
     */
    private static void printStrLen(StrLengthFunction sl,String value) {
        System.out.println(sl.length(value));
    }
    
    @FunctionalInterface
    interface StrLengthFunction {
        public int length(String str);
    }

}

通过上面一个例子,我们获取了字符串的长度,并且通过Lamdba表达式将其打印出来,这里大家看起来似乎比较的麻烦,但是这个实例提供一种操作。下面我们将看一个基于Lamdba处理列表的实例。

Lamdba表达式的具体实例

首先创建一个实体类Student

public class Student {
    private String name;
    private String no;
    private int age;
    
    public Student() {}
    
    public Student(String name, String no, int age) {
        super();
        this.name = name;
        this.no = no;
        this.age = age;
    }
  /.省略getter和setter./
}

接着创建一个列表

public class TestStudent {
    public static void main(String[] args) {
        List<Student> stuList = Arrays.asList(
                new Student("张三","001",19),
                new Student("李四","005",22),
                new Student("王五","010",14),
                new Student("赵六","004",18),
                new Student("何琦","006",12)
                );
    }
}

我们先按照传统的方式来完成如下三个操作,根据用户no排序,列表所有的用户信息,显示年龄大于15岁的所有学生。

这三个操作如果不使用Lamdba表达式非常简单,比较大小只要通过Collections.compare()方法即可完成,第二个参数是一个Comparator的接口,我们通过内部类来实现,下面是三个函数的代码

//根据学号排序
    public static void sortByNo(List<Student> list) {
        Collections.sort(list,new Comparator<Student>() {
            public int compare(Student o1, Student o2) {
                return o1.getNo().compareTo(o2.getNo());
            };
        });
    }
    //列表所有学生信息
    public static void listAll(List<Student> stuList) {
        for(Student stu:stuList) {
            System.out.println(stu);
        }
        
    }
    //过滤小于15岁的学生
    public static void filterAge(List<Student> list) {
        for(Student stu:list) {
            if(stu.getAge()>15) {
                System.out.println(stu);
            }
        }
    }

已上三个代码都是以硬编码的方式实现,这种所带来的问题是不灵活,特别是filterAge,在此处其实除了过滤年龄,我们还可能会有其他的条件过滤需求,所以可以通过传入一个条件接口来实现,看看下面改造后的代码

public static void filterCondition(List<Student> stuList,Condition<Student> c) {
  for(Student stu:stuList) {
    if(c.test(stu)) {
      System.out.println(stu);
    }
  }
}

interface Condition<T> {
  public boolean test(T t);
}

增加了一个Condition的接口,来传入条件,当满足条件之后就执行打印,这个条件可以通过匿名内部类的方式来实现

filterCondition(stuList, new Condition<Student>() {
  @Override
  public boolean test(Student t) {
    if(t.getAge()>15) return true;
    return false;
  }
});

已上实现了这个操作,这种方式看起来复杂,但是它提供了一个条件检测,只要用不同的方式Condition接口,就可以根据条件来处理不同的信息,这和java中File类的FileFilter非常类似。

以下是该版本的完整代码

public class TestNewStudentByJdk7 {
    public static void main(String[] args) {
        List<Student> stuList = Arrays.asList(
                new Student("张三","001",19),
                new Student("李四","005",22),
                new Student("王五","010",14),
                new Student("赵六","004",18),
                new Student("何琦","006",12)
                );
        sortByNo(stuList);
        listAll(stuList);
        System.out.println("-------------");
        filterCondition(stuList, new Condition<Student>() {
            @Override
            public boolean test(Student t) {
                if(t.getAge()>15) return true;
                return false;
            }
        });
    }
    
    //根据学号排序
    public static void sortByNo(List<Student> list) {
        Collections.sort(list,new Comparator<Student>() {
            public int compare(Student o1, Student o2) {
                return o1.getNo().compareTo(o2.getNo());
            };
        });
    }
    //列表所有学生信息
    public static void listAll(List<Student> stuList) {
        for(Student stu:stuList) {
            System.out.println(stu);
        }
        
    }
    
    public static void filterCondition(List<Student> stuList,Condition<Student> c) {
        for(Student stu:stuList) {
            if(c.test(stu)) {
                System.out.println(stu);
            }
        }
    }
    
    interface Condition<T> {
        public boolean test(T t);
    }
}

下面我们将看一下Lamdba的实现方式,首先看sortByName方法,Comparator接口中只有一个compare的函数,所以我们可以使用Lamdba来简化这一部分的操作。

//根据学号排序
public static void sortByNo(List<Student> list) {
  Collections.sort(list,(s1,s2)->s1.getNo().compareTo(s2.getNo()));
}

接下来通过Lamdba的方式来操作filter方法,这个使用了Lamdba之后会简化非常多的操作,看如下三个例子

//查询所有年龄大于15的人
filterCondition(stuList,(t)->t.getAge()>15);
//查询姓张的所有学生
filterCondition(stuList,(t)->t.getName().startsWith("张"));
//查询所有学生对象,条件为true
filterCondition(stuList, (t)->true);

可见使用了Lamdba之后整个代码得到了极大的精简,看一下完整的代码

public class TestStudentByLamdba {
    public static void main(String[] args) {
        List<Student> stuList = Arrays.asList(
                new Student("张三","001",19),
                new Student("李四","005",22),
                new Student("王五","010",14),
                new Student("赵六","004",18),
                new Student("何琦","006",12)
                );
        sortByNo(stuList);
        System.out.println("age > 15");
        //查询所有年龄大于15的人
        filterCondition(stuList,(t)->t.getAge()>15);
        System.out.println("name like 张");
        //查询姓张的所有学生
        filterCondition(stuList,(t)->t.getName().startsWith("张"));
        System.out.println("all student");
        //查询所有学生对象,条件为true
        filterCondition(stuList, (t)->true);
    }
    
    //根据学号排序
    public static void sortByNo(List<Student> list) {
        Collections.sort(list,(s1,s2)->s1.getNo().compareTo(s2.getNo()));
    }
    
    public static void filterCondition(List<Student> stuList,Condition<Student> c) {
        for(Student stu:stuList) {
            if(c.test(stu)) {
                System.out.println(stu);
            }
        }
    }
    
    interface Condition<T> {
        public boolean test(T t);
    }
}

关于Lamdba的讲解第一部分就到这里,相信通过这些实例,会让大家对Lamdba有一个基本的认识,下一部分将会讲解一些相对深入的知识。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容