前言:
而今我们对“java 传入函数”大致比较着重,同学们都想要学习一些“java 传入函数”的相关文章。那么小编在网上网罗了一些对于“java 传入函数””的相关资讯,希望姐妹们能喜欢,小伙伴们快快来学习一下吧!一、什么是lambda表达式
lambda表达式是Java8的新特性,也被称为箭头函数,或者闭包,它所体现的是一种轻量级函数式的编程思想,核心是行为参数化,可以把一段代码像值一样传递给方法,传入不同的代码实现不同的功能。
Lambda就是匿名的行为参数化的一种语法实现,它没有名称,但它有参数列表、函数主体、返回类型,甚至是抛出异常。它的语法格式如下:
(parameters) -> expression
或 (parameters) ->{ statements; }
以下是lambda表达式的重要特征:
可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。可选的大括号:如果主体包含了一个语句,就不需要使用大括号。可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
下面是一些简单的例子:
(parameters) -> expression默认Return的,expression只能是一句代码(parameters) -> (statements)没有默认Retrun,statements可以是多行代码下面是一些// 1. 不需要参数,返回值为 5 () -> 5 // 2. 接收一个参数(数字类型),返回其2倍的值 x -> 2 * x // 3. 接受2个参数(数字),并返回他们的差值 (x, y) -> x – y // 4. 接收2个int型整数,返回他们的和 (int x, int y) -> x + y // 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void) (String s) -> {System.out.print(s)
使用lambda来定义函数接口的实现,和Java8之前的匿名函数相比,代码看起来更加紧凑和优美。
例如:
/** * Java8之前的通过匿名函数来定义接口实现 */ Runnable runnable1 = new Runnable() { @Override public void run() { System.out.println("Just for test1"); } }; /** * Lambda来定义函数接口实现 */ Runnable runnable2 = ()->{ System.out.println("Just for test2"); };
但是如果statements很长,我们不应该用Lambda,而应该单独定义一个方法,然后使用方法引用的方式可以提高代码可读性。
二、函数式接口
函数式接口是指只定义一个抽象方法的接口,它允许有default方法。
为了更好的描述该接口是函数式接口,你可以在接口上添加@FunctionalInterface注解。
例如:
private static void testDefineFunctionInterfaceByLambda() { Runnable runnable = ()->{ System.out.println("hello, run"); }; runnable.run();}
函数式的接口的实现可以用lambda表达式来定义,每一个该类型的lambda表达式都会被匹配到接口中唯一的抽象方法。
例如:
private static void testDefineFunctionInterfaceByLambda() { Runnable runnable = ()->{ System.out.println("hello, run"); }; runnable.run();}
为了更好的推进java的函数式编程,Java8在 java.util.function 包下为我们提供了大量好用的函数式,由于基本数据类型不能抽象成对象,所以在java.util.function你可以看到有大量Double,Int,Long等前缀的函数式接口。
三、lambda使用外层局部变量
1. lambda 表达式能引用标记了 final 的外层局部变量,但不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。
2. lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义)。
3. lambda表达式内部可以使用类的实例变量,并且可以修改非final的实例变量的值。
例如:
String hello = "Hello,"; private void testLambdaVariableScope(){ /** * 例子1:引用并修改类的非fina内部l变量 */ //输出 'instance.hello_var:hello' System.out.println("instance.hello_var:" + hello); Consumer<String> consumer1 = (t) -> { System.out.println(hello + t); hello = "Nihao"; }; //输出'Hello,Jean K' consumer1.accept("Jean K"); //输出 'instance.hello_var:nihao' System.out.println("instance.hello_var:" + hello); /** * 例子2:引用方法内lambda外的final变量 */ final String name = "Tom H"; Consumer<String> consumer2 = (t) -> { System.out.println(t + name); //这里会报错,报错信息:Variable used in lambda expression should be final or effectively final. //name = "KK One"; }; //输出'Hello,Tom H' consumer2.accept("Hello,"); }
输出结果:
instance.hello_var:Hello,Hello,Jean Kinstance.hello_var:NihaoHello,Tom H四、lambda表达式的异常处理
函数式接口的抽象方法可以声明受检异常(checked exception),调用函数式接口对象的方法必须catch这个异常,或者在throws中声明抛出的异常类。
例如:
@FunctionalInterface interface ClientService{ void sayHello(String name) throws Exception; }private static void testLambdaCheckedException() { /** * 例子1:自定义函数式接口,在抽象方法中定义受检异常 */ ClientService clientService = (s)->{ System.out.println("Hello, " + s); throw new Exception("break it manually"); }; try { clientService.sayHello("Jim L"); } catch (Exception e) { e.printStackTrace(); }}
Java8的java.util.function包下定义的很多函数式接口,但这些函数式接口清一色不支持受检异常(checked exception),因此不能把抛出受检异常的lambda表达式赋予给对应的函数式接口实例,必须在lambda表达式内部try/catch来处理受检异常(checked exception),如果需要继续抛出异常,必须自己把受检异常(checked exception)转换成非受检异常(unchecked exception),然后重新抛出。
例如:
private static void testLambdaUncheckException(){ Function<String, URL> function = (s)->{ try { return new URL(s); }catch (MalformedURLException e){ throw new RuntimeException("Malformed URL:" + s, e); } }; try { URL url = function.apply("http//abc"); System.out.println("url:" + url); }catch (RuntimeException e){ e.printStackTrace(); } }
输出结果:
java.lang.RuntimeException: Malformed URL:http//abc at org.laolang.jdk8.lambda.LambdaTest.lambda$testLambdaUncheckException$4(LambdaTest.java:91) at org.laolang.jdk8.lambda.LambdaTest.testLambdaUncheckException(LambdaTest.java:95) at org.laolang.jdk8.lambda.LambdaTest.main(LambdaTest.java:17)Caused by: java.net.MalformedURLException: no protocol: http//abc at java.base/java.net.URL.<init>(URL.java:672) at java.base/java.net.URL.<init>(URL.java:568) at java.base/java.net.URL.<init>(URL.java:515) at org.laolang.jdk8.lambda.LambdaTest.lambda$testLambdaUncheckException$4(LambdaTest.java:89) ... 2 more
Tips: 受检异常(checked exception)是指Exception及其子类,必须在方法定义的throws中声明或者方法体内使用try/catch进行捕获处理;非受检异常(unchecked exception)是指RuntimeException及其子类,不需要在方法定义的throws中声明或者方法体内使用try/catch进行捕获处理。
五、方法引用
对于函数式接口,除了可以通过lambda进行赋值,还可以通过方法引用来进行赋值。方法引用的目的是为了有效利用已经存在的方法。被引用的方法,必须和函数式接口中的抽象方法保持相同的的输入参数个数,类型和顺序,相同返回类型,以及相同的异常声明列表。
方法引用的表示形式:
1. 实例对象名::实例方法名
2. 类名::静态方法名
3. 类名::实例方法名
4. 类名::new
例如:
private static void testMethodReference(){ /** * 例子1:引用类的静态方法 */ Function<String,Integer> function = Integer::parseInt; Integer integer = function.apply("12345"); /** * 例子2:引用实例方法 */ Random random = new Random(); Supplier<Long> supplier = random::nextLong; System.out.println("supplier.get:" + supplier.get()); List<String> list1 = Arrays.asList("A","B"); list1.forEach(System.out::println); List<String> lowCaseList1 = list1.stream().map(String::toLowerCase).collect(Collectors.toList()); /** * 例子3:引用无参数构造函数 */ Supplier<Person> personSupplier = Person::new; Person person1 = personSupplier.get(); /** * 例子4:引用只有一个参数的构造函数 */ Function<String,Person> personFunction = Person::new; Person person2 = personFunction.apply("Jack K"); /** * 例子5:引用两个参数的构造函数 */ BiFunction<String,Integer,Person> personBiFunction = Person::new; Person person3 = personBiFunction.apply("Jean Y", 30); } public static class Person{ private String name; private Integer age; public Person(){} public Person(String name){ this.name = name; } public Person(String name, Integer age){ this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }
lambda及其等效方法引用的例子:
(Person a) -> a.getWeight() 等效于 Person::getWeight
() -> Thread.currentThread().getName() 等效于 Thread.currentThread()::getName
(str, i) -> str.substring(i) 等效于 String::substring
(String s) -> System.out.println(s) 等效于 System.out::println
六、复合lambda
复合lambda是指可以把多个lambda表达式进行合并,从而形成一个新的lambda表达式。在java8中,一般都是通过在函数式接口中提供一系列的默认方法来对多个lambda进行合并形成新的lambda。
例如:
private static void testCombinedLambda() { List<Person> personList = new ArrayList<>(); /** * 例子1:Comparator(比较器复合) */ personList.add(new Person("Jack B",40)); personList.add(new Person("Sam J", 50)); personList.add(new Person("Wayne D", 34)); personList.add(new Person("Wayne D", 20)); Comparator<Person> personComparator = Comparator.comparing(Person::getName); personList.sort(personComparator.reversed().thenComparing(Person::getAge)); /** * 例子2:Predicate(谓词复合) */ Predicate<Person> personPredicate = (p) -> p.getName().equals("Wayne D"); personPredicate = personPredicate.and(p->p.getAge()>21); personList.stream().filter(personPredicate).forEach(System.out::println); /** * 例子3:Function(函数复合) * 假设有一个函数f给数字加1 (x -> x + 1),另一个函数g给数字乘2,你可以将它们组合成一个函数h,先给数字加1,再给结果乘2 */ Function<Integer, Integer> f = x -> x + 1; Function<Integer, Integer> g = x -> x * 2; Function<Integer, Integer> h = f.andThen(g); //输出5 System.out.println(h.apply(1)); }七、参考资料
Java 8 Lambda 表达式
Java 8 Functional Interfaces and Checked Exceptions
Throwing Checked Exceptions From Lambdas
标签: #java 传入函数