Java8新特性(二)Lambda表达式

参考
英文相关文档
Lambda
lambda表达式详解



一、示例引入

我们将带有参数变量的表达式,称之为Lambda表达式,先看一个基本的示例,对其有一个基本的认识。
class User{
    public String name;
    public int score;
    public User(String name, int score) {
        super();
        this.name = name;
        this.score = score;
    }
    @Override
    public String toString() {
        return "User [name=" + name + ", score=" + score + "]";
    }
}

    @Test
    public void arrayComparableTest(){
        User[] us = new User[]{new User("张三", 10), new User("李四", 15), new User("王五", 12)};
        //以前的写法
        Arrays.sort(us, new Comparator<User>() {
            // 对数组中的元素进行排序
            @Override
            public int compare(User o1, User o2) {
                return Integer.compare(o1.score, o2.score);
            }
        });
        
        //分析:
        //1. sort方法,第二个参数,一定是一个Comparator接口。
        //2. Comparator接口,一定需要实现一个compare方法。
        //3. compare方法,一定需要返回一个int类型的结果。
        
        //优化1  使用Lambda表达式,将一定、必不可少的东西删除了,让编译器自己去推导出结果
        Arrays.sort(us, (User o1, User o2) -> { return Integer.compare(o1.score, o2.score); });

        //优化2  compare方法中只有一行代码,并且,这一行代码,一定会返回一个int类型的值。那么,我们去删除它。
        Arrays.sort(us, (User o1, User o2)-> Integer.compare(o1.score, o2.score));

        //优化3 Compare方法,有两个参数,这两个参数的类型,可以通过传递的数组来推导元素类型,一定是User类型。那么,我们删除它
        Arrays.sort(us, (o1, o2)-> Integer.compare(o1.score, o2.score));


        //优化4 因为java8的comparator接口中有comparing静态方法,它可以像下面这样用:
        Arrays.sort(us, Comparator.comparingInt((o) -> o.score));
        
        //优化5 方法引用
        Arrays.sort(us, Comparator.comparingInt(User::getScore));

        System.out.println(Arrays.toString(us));
    }


二、Lambda表达式基本语法

java中,引入了一个新的操作符 ->,该操作符在很多资料中,称为箭头操作符,或者lambda操作符;
Lambda表达式的基本语法:方法参数列表 -> 表达式
箭头操作符将lambda分成了两个部分:

1.   左侧:lambda表达式的参数列表
2.   右侧:lambda表达式中所需要执行的功能,即lambda函数体

参数列表:是接口的方法参数列表
表达式:是接口方法的具体逻辑实现。

1、参数列表

1.1、方法没有参数
没有参数,需要注意的是,参数列表的 () 是一定不能省略的。因为括号才表示是方法的参数列表。

示例
new Thread(() -> System.out.println("没有参数")).start();

1.2、方法有一个参数

  • 如果参数写类型,那么,必须写方法的()
Frame f = new Frame("lambda");
f.setBounds(100, 100, 100, 300);
Button b = new Button("按钮");
b.addActionListener((ActionEvent event) -> System.out.println("按钮被点击"));
f.setVisible(true);

  • 如果参数不写类型(编译器可以推导出来参数的类型),那么,可以省略方法的()
Frame f = new Frame("lambda");
f.setBounds(100, 100, 100, 300);
Button b = new Button("按钮");
b.addActionListener(event -> System.out.println("按钮被点击"));
f.setVisible(true);

1.3、方法有两个或多个参数
如果是两个或者多个参数,那么需要注意,不管是否有参数类型,都必须有()

Integer[] arr = new Integer[]{1,5,3,4};
Arrays.sort(arr, (x, y) -> Integer.compare(x, y));
System.out.println(Arrays.toString(arr));

1.4、如果有其他的修饰符修饰
可以用final修饰局部变量,此时,如果要写额外的修饰符的时候,参数必须得有类型。不管是一个参数还是多个参数。

Integer[] arr = new Integer[]{1,5,3,4};
Arrays.sort(arr, (final Integer x, final Integer y) -> Integer.compare(x, y));
System.out.println(Arrays.toString(arr));

2、表达式

  • 如果表达式只有一行代码
    可以不需要{},直接写,不能写return关键字。编译器帮助我们推导return。
  • 如果表达式超过一行
    那么这一段代码,就必须写{},就必须遵循方法的规则,并且如果方法有返回,就必须有返回语句。

3、变量作用域

this,在匿名内部类中,如果我们使用this,那么,这个this其实是匿名内部类对自己的引用。但是,在Lambda表达式中的this,却是指向的该Lambda表达式所在方法,这个方法所在的类的引用。这一点需要跟以前的情况区分开。

即:
匿名类中的this是“匿名类对象”本身;
Lambda表达式中的this是“调用Lambda表达式的对象”。

三、方法引用

方法引用我们可以把它看做是 仅仅调用特定方法的Lambda的一种快捷写法。
示例:

(Apple a) -> a.getWeight()			 			 Apple::getWeight
() -> Thread.currentThread().dumpStack()		 Thread.currentThread()::dumpStack
(str, i) -> str.substring(i) 					 String::substring
(String s) -> System.out.println(s) 			 System.out::println

这里,我们引入一种新的语法结构 ::,如果Lambda表达式中,接口方法的实现,只调用其他方法来真正完成逻辑,并且接口方法的参数原封不动的传递给了那个完成逻辑的方法,那么我们就可以使用这种语法。

在Lambda表达式中,支持三种方法引用:

  1. 类 :: 静态方法
(args) -> ClassName.staticMethod(args)
//可以写成
ClassName::staticMethod

  1. 对象 :: 普通方法
(arg0, rest) -> arg0.instanceMethod(rest)
//可以写成
ClassName::instanceMethod

示例一:(系统方法)

//例如
@Test
public void test3(){
    Integer[] arr = new Integer[]{1,5,3,4};
    // 对象 :: 普通方法
    Arrays.sort(arr, this::myCompare);
    System.out.println(Arrays.toString(arr));
}
public int myCompare(int x, int y){
    return Integer.compare(x, y);
}

示例二:(自定义)

// 定义的函数式接口
    @FunctionalInterface
    public interface IWork {
        void work(int x, int y);// 接口方法没有返回值。
    }

    public class lambdatest {
        public void wrap(IWork work){
            System.out.println("some work");
            work.work(3, 4);
        }
        @Test
        public void test1(){
//            IWork work = this::getSum;
//            IWork work2 = (x,y)-> getSum(x,y);
            this.wrap(this::getSum);// 使用对象来调用普通方法,注意,这个方法是有返回值的。
        }
        public int getSum(int x, int y){
            System.out.println(x + y);
            return x + y;
        }
    }


构造函数示例:

//不带参数的构造函数
Supplier<Apple> c1 = Apple::new;
Apple a1 = c1.get();
//等价于
Supplier<Apple> c1 = () -> new Apple();
Apple a1 = c1.get();


//带参数的构造函数
Function<Integer, Apple> c2 = Apple::new;
Apple a2 = c2.apply(110);
//等价于
Function<Integer, Apple> c2 = (weight) -> new Apple(weight);
Apple a2 = c2.apply(110);


//带有两个参数的构造函数
iFunction<String, Integer, Apple> c3 = Apple::new;
Apple c3 = c3.apply("green", 110);
//等价于
BiFunction<String, Integer, Apple> c3 =(color, weight) -> new Apple(color, weight);
Apple c3 = c3.apply("green", 110);


//不将构造函数实例化,但可以引用它,这有一些有趣的应用,如下:
static Map<String, Function<Integer, Fruit>> map = new HashMap<>();
static {
	map.put("apple", Apple::new);
	map.put("orange", Orange::new);
	// etc...
}
//利用构造函数引用,新创建了一个方法,用两个参数可以得到具有制定重量的不同水果
public static Fruit giveMeFruit(String fruit, Integer weight){
	return map.get(fruit.toLowerCase())
			  .apply(weight);
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值