Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Java8 functional programming and example Analysis of New feature lambda

2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/01 Report--

Editor to share with you Java8 functional programming and the new features of lambda example analysis, I believe that most people do not know much about it, so share this article for your reference, I hope you will learn a lot after reading this article, let's go to understand it!

Why use functional programming?

Functional programming is more of a programming way of thinking and a methodology. The main difference between functional programming and imperative programming is that functional programming tells the code what you want to do, while imperative programming tells the code what to do. To put it bluntly, functional programming is based on a certain syntax or calling API. For example, we now need to find the smallest number from a set of numbers, and if we use imperative programming to implement this requirement, the code is as follows:

Public static void main (String [] args) {int [] nums = new int [] {1,2,3,4,5,6,7,8}; int min = Integer.MAX_VALUE; for (int num: nums) {if (num)

< min) { min = num; } } System.out.println(min);} 而使用函数式编程进行实现的话,所编写的代码如下: public static void main(String[] args) { int[] nums = new int[]{1, 2, 3, 4, 5, 6, 7, 8}; int min = IntStream.of(nums).min().getAsInt(); System.out.println(min);} 从以上的两个例子中,可以看出,命令式编程需要自己去实现具体的逻辑细节。而函数式编程则是调用API完成需求的实现,将原本命令式的代码写成一系列嵌套的函数调用,在函数式编程下显得代码更简洁、易懂,这就是为什么要使用函数式编程的原因之一。所以才说函数式编程是告诉代码你要做什么,而命令式编程则是告诉代码要怎么做,是一种思维的转变。 说到函数式编程就不得不提一下lambda表达式,它是函数式编程的基础。在Java还不支持lambda表达式时,我们需要创建一个线程的话,需要编写如下代码: public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { System.out.println("running"); } }).start();} 而使用lambda表达式一句代码就能完成线程的创建,lambda强调了函数的输入输出,隐藏了过程的细节,并且可以接受函数当作输入(参数)和输出(返回值): public static void main(String[] args) { new Thread(() ->

System.out.println ("running") .start ();

Note: the left side of the arrow is input and the right side is output.

The lambda expression actually returns the implementation object of the Runnable interface, which is similar to calling a method to get the instance object, except that the implementation code is written directly in the lambda expression. We can make a simple comparison:

Public static void main (String [] args) {Runnable runnable1 = ()-> System.out.println ("running"); Runnable runnable2 = RunnableFactory.getInstance ();} New features of JDK8 interface

1. Function interface, which can only have one method that needs to be implemented, which can be declared using the @ FunctionalInterface annotation. As follows:

@ FunctionalInterfaceinterface Interface1 {int doubleNum (int I);}

Use lambda expressions to get several ways to write an implementation example of this interface:

Public static void main (String [] args) {/ / the most common way of writing is Interface1 i1 = (I)-> I * 2; Interface1 i2 = I-> I * 2; / / you can specify the parameter type Interface1 i3 = (int I)-> I * 2; / / if you have multiple lines of code, you can write Interface1 i4 = (int I)-> {System.out.println (I); return I * 2 };}

two。 One of the more important interface features is the default method of the interface, which provides the default implementation. The default method, like the method of a normal implementation class, can use keywords such as this:

@ FunctionalInterfaceinterface Interface1 {int doubleNum (int I); default int add (int x, int y) {return x + y;}}

The default method feature is important because we can provide a default implementation on some previously written interfaces without affecting any implementation classes or existing code. For example, the List interface that we are most familiar with, the List interface has not changed any code since JDK1.2, and some default implementations have been added to this new feature after 1.8. This is because if there is no feature of the default method, the impact of modifying the interface code is huge, and with the default method, adding the default implementation can not affect any code.

3. When multiple interfaces inherit, the problem of default method overrides may occur. You can specify which interface's default method implementation to use, as shown in the following example:

@ FunctionalInterfaceinterface Interface1 {int doubleNum (int I); default int add (int x, int y) {return x + y;}} @ FunctionalInterfaceinterface Interface2 {int doubleNum (int I); default int add (int x, int y) {return x + y }} @ FunctionalInterfaceinterface Interface3 extends Interface1, Interface2 {@ Override default int add (int x, int y) {/ / specify which interface's default method to use to implement return Interface1.super.add (x, y);}} function interface

In this section, let's take a look at what important function interfaces are included in JDK8:

You can see that there are several interfaces in the above table, and the most commonly used one is the Function interface, which can save us from defining some unnecessary function interfaces and reduce the number of interfaces. Let's use a simple example to demonstrate the use of the Function interface:

Import java.text.DecimalFormat;import java.util.function.Function;class MyMoney {private final int money; public MyMoney (int money) {this.money = money;} public void printMoney (Function moneyFormat) {System.out.println ("my savings:" + moneyFormat.apply (this.money));}} public class MoneyDemo {public static void main (String [] args) {MyMoney me = new MyMoney (99999999) Function moneyFormat = I-> new DecimalFormat ("#, #") .format (I); / / function interface supports chained operations, such as adding a string me.printMoney (moneyFormat.andThen (s-> "RMB" + s));}}

Run the above example, and the console output is as follows:

My deposit: RMB 99999999

If the Function interface is not used in this example, you need to define a function interface and chain operation is not supported, as shown in the following example:

Import java.text.DecimalFormat;// customizes a function interface @ FunctionalInterfaceinterface IMoneyFormat {String format (int I);} class MyMoney {private final int money; public MyMoney (int money) {this.money = money;} public void printMoney (IMoneyFormat moneyFormat) {System.out.println ("my savings:" + moneyFormat.format (this.money)) }} public class MoneyDemo {public static void main (String [] args) {MyMoney me = new MyMoney (99999999); IMoneyFormat moneyFormat = I-> new DecimalFormat ("#, # #") .format (I); me.printMoney (moneyFormat);}}

Then let's take a look at the use of Predicate interface and Consumer interface, as shown in the following example:

Public static void main (String [] args) {/ / assertion function interface Predicate predicate = I-> I > 0; System.out.println (predicate.test (- 9)); / / consumption function interface Consumer consumer = System.out::println; consumer.accept ("this is the input data");}

Run the above example, and the console output is as follows:

False

This is the input data.

These interfaces generally have encapsulation for basic types, so there is no need to specify generics using specific types of interfaces, as shown in the following example:

Public static void main (String [] args) {/ / assertion function interface IntPredicate intPredicate = I-> I > 0; System.out.println (intPredicate.test (- 9)); / / consumption function interface IntConsumer intConsumer = (value)-> System.out.println ("input data is:" + value); intConsumer.accept (123);}

Run the above code, and the console output is as follows:

False

The data entered is: 123

With the preparation of the above interface example, we should have a preliminary understanding of the use of function interfaces. Next, we will demonstrate the use of the remaining function interfaces:

Public static void main (String [] args) {/ / provide data interface Supplier supplier = ()-> 10 + 1; System.out.println ("data provided is:" + supplier.get ()); / / unary function interface UnaryOperator unaryOperator = I-> I * 2; System.out.println ("calculation result is:" + unaryOperator.apply (10)) / / binary function interface BinaryOperator binaryOperator = (a, b)-> a * b; System.out.println ("calculation result:" + binaryOperator.apply (10,10));}

Run the above code, and the console output is as follows:

The data provided is: 11

The calculated result is: 20

The calculated result is: 100

The BiFunction API only has one more input than the Function API, as shown below:

Class MyMoney {private final int money; private final String name; public MyMoney (int money, String name) {this.money = money; this.name = name;} public void printMoney (BiFunction moneyFormat) {System.out.println (moneyFormat.apply (this.money, this.name));}} public class MoneyDemo {public static void main (String [] args) {MyMoney me = new MyMoney (99999999, "Xiaoming") Deposit for BiFunction moneyFormat = (I, name)-> name + "+ new DecimalFormat (" #, # ") .format (I); me.printMoney (moneyFormat);}}

Run the above code, and the console output is as follows:

Xiaoming's deposit: 99999999

Method reference

After learning lambda expressions, we usually use lambda expressions to create anonymous methods. But sometimes we just need to call an existing method. The following is an example:

Arrays.sort (stringsArray, (S1, S2)-> s1.compareToIgnoreCase (S2))

In jdk8, we can abbreviate this lambda expression with a new feature. The following is an example:

Arrays.sort (stringsArray, String::compareToIgnoreCase)

This feature is called method reference (Method Reference). The standard form of a method reference is: class name:: method name. Note: you only need to write the method name, not parentheses.

Currently, there are four forms of method references:

The Lambda expression corresponding to the type sample code example refers to the static method ContainingClass::staticMethodNameString::valueOf (s)-> String.valueOf (s) references an object's instance method containingObject::instanceMethodNamex::toString () ()-> this.toString () refers to the instance method ContainingType::methodNameString::toString (s) of any object of a type-> s.toString reference constructor ClassName::newString::new ()-> newString ()

Let's use a simple example to demonstrate several writing methods of method references. First define an entity class:

Public class Dog {private String name = "Erha"; private int food = 10; public Dog () {} public Dog (String name) {this.name = name;} public static void bark (Dog dog) {System.out.println (dog + "call");} public int eat (int num) {System.out.println ("eat" + num + "jin") This.food-= num; return this.food;} @ Override public String toString () {return this.name;}}

Call the method in the entity class through the method reference, as follows:

Package org.zero01.example.demo;import java.util.function.*;/** * @ ProjectName demo * @ Author: zeroJun * @ Date: 2018-9-21 13:09 * @ Description: method reference demo * / public class MethodRefrenceDemo {public static void main (String [] args) {/ / method reference, call print method Consumer consumer = System.out::println; consumer.accept ("received data") / / static method reference, Consumer consumer2 = Dog::bark; consumer2.accept (new Dog ()) can be called by class name; / / instance method reference, Dog dog = new Dog () through object instance; IntUnaryOperator function = dog::eat; System.out.println ("left" + function.applyAsInt (2) + "jin") / / another way of referencing through the instance method. This can be done because JDK passes the current instance to the non-static method by default, and the parameter name is this, and the parameter position is the first, so we can only access this in the non-static method, so we can pass in the instance object through BiFunction to reference the instance method Dog dog2 = new Dog (); BiFunction biFunction = Dog::eat System.out.println ("left" + biFunction.apply (dog2, 2) + "jin"); / / method reference of no-parameter constructor, similar to static method reference, you only need to analyze the input and output to Supplier supplier = Dog::new; System.out.println ("create a new object:" + supplier.get ()) / / the method with a parameter constructor refers to Function function2 = Dog::new; System.out.println ("created a new object: + function2.apply (" Wangcai ");}}

The last thing to say is to try not to use lambda expressions where you can use method references, so that you won't generate a function like lambda$0, which can reduce the overhead of some resources.

Type inference

From the above example, we know that the reason why Lambda expressions can be used is that there must be a corresponding function interface. This is consistent with the fact that Java is a strongly typed language, which means you can't write Lambda expressions willfully anywhere in the code. In fact, the type of Lambda is the type of the corresponding function interface. Another basis for Lambda expressions is the type inference mechanism, where the compiler can infer the type of the parameter table with sufficient context information without the need for explicit naming.

So the type of the Lambda expression is inferred from the context of the Lambda, where the type required by the Lambda expression is called the target type, as shown in the following figure:

Next, let's use a simple example to demonstrate several type inferences of Lambda expressions, first defining a simple function interface:

@ FunctionalInterfaceinterface IMath {int add (int x, int y);}

The sample code is as follows:

Public class TypeDemo {public static void main (String [] args) {/ / 1. Define IMath iMath = (x, y)-> x + y; / / 2 by variable type. IMath [] iMaths = {(x, y)-> x + y}; / / 3. Object object = (IMath) (x, y)-> x + y; / / 4. Determine the type IMath result = createIMathObj (); / / 5 by returning the value of the method. Determine the type test ((x, y)-> x + y);} public static IMath createIMathObj () {return (x, y)-> x + y;} public static void test (IMath iMath) {return;}} variable references by method parameters

Lambda expressions are similar to inner classes or anonymous classes that implement the specified interface, so referencing variables in Lambda expressions is the same as our rules for referencing variables in anonymous classes. The following is an example:

Public static void main (String [] args) {String str = "current system timestamp is:"; Consumer consumer = s-> System.out.println (str + s); consumer.accept (System.currentTimeMillis ());}

It is worth mentioning that before JDK1.8, we usually set the external variable accessed in the anonymous class to final, while in JDK1.8, the external variable accessed in this anonymous class is set to final by default. For example, if I change the value of the str variable now, ide will prompt an error:

As for why variables should be set to final, this is because there is no reference passing in Java, variables are passed by values. If the variable is not set to final, if the reference to the external variable is changed, the final result will be wrong.

Let's briefly demonstrate the difference between value passing and reference passing with a set of pictures. In the case of a list, for example, when it is just a value pass, the reference to an external variable in an anonymous class is a value object:

If the list variable points to another object at this time, the reference in the anonymous class is the same as the previous value object, so we need to set it to final to prevent external variable references from changing:

If the reference is passed, the reference to the external variable in the anonymous class is not a value object, but a pointer to the external variable:

So even if the list variable points to another object, the reference in the anonymous class changes as the reference of the external variable changes:

Cascading expressions and Corialization

In functional programming, functions can either receive or return other functions. A function is no longer just a factory or generator of an object like in traditional object-oriented programming, it can also create and return another function. The function that returns the function can become a cascading lambda expression, and it is worth noting that the code is very short. Although this grammar may seem strange at first glance, it has its own purpose.

A cascading expression is a combination of multiple lambda expressions. This involves the concept of a higher-order function. A higher-order function is a function that can return a function, as shown in the following example:

/ / the cascading expression Function function1 = x-> y-> x + y _ System.out.println ("the result is:" + function1.apply (2). Apply (3)) is implemented; / / the result is: 5

The y-> x + y here is returned to the upper-level expression as a function, so the output of the first-level expression is the function y-> x + y, which may be easier to understand if enclosed in parentheses:

X-> (y-> x + y)

Cascading expressions can achieve function Corialization. To put it simply, Corialization is to convert a function with multiple parameters into a function with only one parameter, as shown in the following example:

Function function2 = x-> y-> z-> x + y + ZipSystem.out.println ("calculation result is: + function2.apply (1) .apply (2) .println (3)); / / calculation result is: 6

If you want to learn the above route, I would like to recommend a framework learning exchange group. Exchange Learning Group number 874811168 will share some videos recorded by senior architects: Spring,MyBatis,Netty source code analysis, principles of high concurrency, high performance, distributed, micro-service architecture, JVM performance optimization, distributed architecture, and so on. You can also get free learning resources, which have benefited a lot.

The purpose of function Corialization is to standardize functions, to combine functions flexibly, to facilitate unified processing, and so on. For example, I can only call the same method in a loop without calling another method to calculate the sum of elements in an array. The code is as follows:

Public static void main (String [] args) {Function f3 = x-> y-> z-> x + y + z; int [] nums = {1,2,3}; for (int num: nums) {if (f3 instanceof Function) {Object obj = f3.apply (num); if (obj instanceof Function) {f3 = (Function) obj } else {System.out.println ("call ends, result:" + obj); / / call ends, result: 6}

Cascading expressions and Corialization are generally not very common in actual development, so you can have a little understanding of their concepts.

These are all the contents of the article "Java8 functional programming and sample analysis of new features lambda". Thank you for reading! I believe we all have a certain understanding, hope to share the content to help you, if you want to learn more knowledge, welcome to follow the industry information channel!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report