如何正确的在Java8中使用lambda表达式
如何正确的在Java8中使用lambda表达式?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。
创新互联主要业务有网站营销策划、成都网站制作、成都做网站、外贸营销网站建设、微信公众号开发、小程序设计、H5高端网站建设、程序开发等业务。一次合作终身朋友,是我们奉行的宗旨;我们不仅仅把客户当客户,还把客户视为我们的合作伙伴,在开展业务的过程中,公司还积累了丰富的行业经验、成都营销网站建设资源和合作伙伴关系资源,并逐渐建立起规范的客户服务和保障体系。
1.基本介绍
lambda表达式,即带有参数的表达式,为了更清晰地理解lambda表达式,先上代码:
1.1 两种方式的对比
1.1.1 方式1-匿名内部类
class Student{ private String name; private Double score; public Student(String name, Double score) { this.name = name; this.score = score; } public String getName() { return name; } public Double getScore() { return score; } public void setName(String name) { this.name = name; } public void setScore(Double score) { this.score = score; } @Override public String toString() { return "{" + "\"name\":\"" + name + "\"" + ", \"score\":\"" + score + "\"" + "}"; } }: @Test public void test1(){ ListstudentList = new ArrayList (){ { add(new Student("stu1",100.0)); add(new Student("stu2",97.0)); add(new Student("stu3",96.0)); add(new Student("stu4",95.0)); } }; Collections.sort(studentList, new Comparator () { @Override public int compare(Student o1, Student o2) { return Double.compare(o1.getScore(),o2.getScore()); } }); System.out.println(studentList); }
代码调用Collections.sort方法对集合进行排序,其中第二个参数是一个匿名内部类,sort方法调用内部类中的compare方法对list进行位置交换,因为java中的参数类型只能是类或者基本数据类型,所以虽然传入的是一个Comparator类,但是实际上可以理解成为了传递compare方法而不得不传递一个Comparator类 ,这种方式显得比较笨拙,而且大量使用的话代码严重冗余,这种情况在java8中通过使用lambda表达式来解决。
lambda表达式专门针对只有一个方法的接口(即函数式接口),Comparator就是一个函数式接口
@FunctionalInterface public interface Comparator{ int compare(T o1, T o2); }
@FunctionalInterface的作用就是标识一个接口为函数式接口,此时Comparator里只能有一个抽象方法,由编译器进行判定。
使用lambda表达式之后方式1 中的代码改造如下
1.1.2 方式2-lambda表达式
public void test1_(){ ListstudentList = new ArrayList (){ { add(new Student("stu1",100.0)); add(new Student("stu2",97.0)); add(new Student("stu3",96.0)); add(new Student("stu4",95.0)); } }; Collections.sort(studentList,(s1,s2)-> Double.compare(s1.getScore(),s2.getScore())); System.out.println(studentList); }
1.2 lambda语法
1.2.1 多参数
(1). lambda表达式的基本格式为(x1,x2)->{表达式...};
(2). 在上式中,lambda表达式带有两个参数,此时参数类型可以省略,但两边的括号不能省略
(3). 如果表达式只有一行,那么表达式两边的花括号可以省略
1.2.2 无参数
一个常见的例子是新建一个线程,不使用lambda表达式的写法为
public void testThread(){ new Thread(new Runnable() { @Override public void run() { System.out.println("hello, i am thread!"); } }).start(); }
其中Runnable接口也是一个函数式接口,源码如下
@FunctionalInterface public interface Runnable { /** * When an object implementing interfaceRunnable
is used * to create a thread, starting the thread causes the object's *run
method to be called in that separately executing * thread. ** The general contract of the method
run
is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run(); }
将其转换为lambda表达式的写法为
public void testThread_(){ new Thread(()-> System.out.println("hello, i am thread!")).start(); }
对于没有参数的情况 :
(1).参数的括号不能省略,
(2).其他语法同多参数
1.2.3 一个参数
我们构造一个只有一个参数的函数式接口
@FunctionalInterface public interface MyFunctionalInterface { public void single(String msg); } /** * 需要单个参数 */ public static void testOnePar(MyFunctionalInterface myFunctionalInterface){ myFunctionalInterface.single("msg"); } /** * 一个参数,可以省略参数的括号 */ @Test public void testOneParameter(){ testOnePar(x-> System.out.println(x)); }
对于一个参数的情况:
(1).可以省略参数的括号和类型
(2).其他语法同多参数
1.3 jdk提供的常用函数式接口
在这里我们为了演示只有一个参数的情况自己创建了一个函数式接口,其实java8中已经为我们提供了很多常见的函数式接口,截图如下:
常见的有
Function:提供任意一种类型的参数,返回另外一个任意类型返回值。 R apply(T t);
Consumer:提供任意一种类型的参数,返回空值。 void accept(T t);
Supplier:参数为空,得到任意一种类型的返回值。T get();
Predicate:提供任意一种类型的参数,返回boolean返回值。boolean test(T t);
因此针对上面的情况,我们可以直接使用Consumer类,
/** * 需要单个参数 */ public static void testOnePar1(Consumer unaryOperator){ unaryOperator.accept("msg"); }
2.方法引用
lambda表达式用于替换函数式接口,方法引用也是如此,方法引用可以使代码更加简单和便捷
2.1 小试牛刀
上代码,根据List中字符串长度排序:
public static void test1_() { ListstrLst = new ArrayList () { { add("adfkjsdkfjdskjfkds"); add("asdfasdfafgfgf"); add("public static void main"); } }; Collections.sort(strLst, String::compareToIgnoreCase); System.out.println(strLst); }
只要方法的参数和返回值类型与函数式接口中抽象方法的参数和返回值类型一致,就可以使用方法引用。
2.2 使用方式
方法引用主要有如下三种使用情况
(1). 类::实例方法
(2). 类::静态方法
(3). 对象::实例方法
其中后两种情况等同于提供方法参数的lambda表达式,
如System.out::println 等同于(x)->System.out.println(x),
Math::pow 等同于(x,y)->Math.pow(x,y).
第一种中,第一个参数会成为执行方法的对象,String::compareToIgnoreCase)等同于(x,y)->x.compareToIgnoreCase(y)
此外,方法引用还可以使用this::methodName及super::methodName表示该对象或者其父类对象中的方法
class Father { public void greet() { System.out.println("Hello, i am function in father!"); } } class Child extends Father { @Override public void greet() { Runnable runnable = super::greet; new Thread(runnable).start(); } } public static void main(String[] args){ new Child().greet(); }
最后打印的结果为:Hello, i am function in father!
3.构造器引用
构造器引用同方法引用类似,同样作用于函数式接口
构造器引用的语法为 ClassName::new
啥也不说,线上代码
Listlabels = Arrays.asList("aaa","bbb","ccc","ddd"); Stream
如上代码所示,map方法内需要一个Function对象
Stream map(Function<? super T, ? extends R> mapper);
调用Button的构造器,接收一个String类型的参数,返回一个Button类型的对象
public class Button extends ButtonBase { /** * Creates a button with the specified text as its label. * * @param text A text string for its label. */ public Button(String text) { super(text); initialize(); } }
另外一个例子如下
Button[] buttons1 = buttonStream.toArray(Button[]::new);
toArray方法的申明如下
接收一个IntFunction类型的接口R apply(int value);该接口接收一个int型参数,返回指定类型
我们在项目中经常使用asList来创建一个ArrayList,但是也只能是ArrayList,
我们如何在asList中指定创建哪种类型的List的实例呢,使用构造器引用使得asList方法可以指定生成的List类型。
代码中doWork1和doWork2分别使用lambda表达式和匿名内部类的方式实现了Runnable接口,最后打印的结果如下
可见使用lambda表达式的方式,表达式中的this指的是包含lambda表达式的类,而使用匿名内部类的方式,this指的是匿名内部类本身。
lambda达式中的变量有几类,1.参数内的变量,2.lambda表达式中的内部变量,3.自由变量,自由变量指的是在lambda表达式之外定义的变量。
对于引用类型(如ArrayList),final指的是引用指向的类始终不变,进行add操作是允许的,但是应该保证变量的线程安全。
java8对于接口做出了种种改进,使得我们可以在接口中实现默认方法和静态方法,见Comparator接口完整定义
对于第一个冲突,java8规定类中的方法优先级要高于接口中的默认方法,所以接口中默认方法复写Object类中的方法是没有意义的,因为所有的接口都默认继承自Object类使得默认方法一定会被覆盖。
对于第二个冲突,java8强制要求子类必须复写接口中冲突的方法。如下所示:
关于如何正确的在Java8中使用lambda表达式问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注创新互联行业资讯频道了解更多相关知识。
网页题目:如何正确的在Java8中使用lambda表达式
网页路径:http://myzitong.com/article/pphjcj.html