Computer scientists place great value on simplicity of expression when solving problems. Unix pioneer Ken Thompson famously said, "The day I throw away 1,000 lines of code is one of my most productive days." This is a well-deserved goal for any software project that requires ongoing support and maintenance. Early Lisp contributor Paul Graham even equated language simplicity with language power. This recognition of power has made the ability to write compact, concise code the primary criterion for language selection in many modern software projects. Any program can be made shorter by refactoring it to remove redundant code or unused placeholders such as spaces, but some languages are naturally expressive and therefore particularly well suited to writing short programs. Recognizing this, Perl programmers popularized the game of code golf; the goal is to solve a particular problem or implement a given algorithm in the shortest possible amount of code. The APL language was designed to allow programmers to write powerful programs with very little code using special graphical notations. Such programs, when implemented properly, map well to standard mathematical expressions. Concise languages are very effective at creating small scripts quickly, especially in simple, well-defined problem domains where the intent is not obscured by the brevity. Java has a reputation for being verbose compared to other programming languages. Much of this is due to conventions in the programming community that favor greater descriptiveness and control when accomplishing tasks. For example, long variable names make large code bases more readable and maintainable in the long run. Descriptive class names often map to file names, making it clear when adding new functionality to an existing system. If maintained consistently, descriptive names can greatly simplify text searches that indicate a specific feature in an application. These practices have made Java extremely successful in large-scale implementations of large, complex code bases. For small projects, brevity is preferred, and some languages are well suited for short scripts or interactive exploratory programming at the command prompt. Java, as a general-purpose language, is better suited for writing cross-platform tools. In this case, the use of "verbose Java" does not necessarily add additional value. Although coding style can be changed in areas such as variable naming, historically, at some basic level, Java has required more characters to accomplish the same task than other languages. In response to these limitations, the Java language has been continuously updated to include features that are often called "syntactic sugar". These idioms can achieve the goal of expressing the same functionality with fewer characters. Compared to their more verbose counterparts, these idioms are more popular with the programming community and are often quickly adopted by the community as common usage. This article will focus on the best practices for writing concise Java code, especially about the new features in JDK 8. In short, the introduction of Lambda expressions in Java 8 makes more elegant code possible. This is especially evident when processing collections with the new Java Streaming API. Verbose JavaJava has a reputation for being verbose, in part, because of its object-oriented implementation style. In many languages, the classic "Hello World" program can be written in a single line of code containing no more than 20 characters. In Java, in addition to the main method included in the class definition, the main method also needs to contain a method call to print a string to the terminal via System.out.println(). Even in the extreme case of using a minimum of method qualifiers, parentheses, and semicolons, and removing all whitespace, the "Hello World" program is a minimum of 86 characters. Add in spaces and indentation for readability, and the Java version of the "Hello World" program is undoubtedly verbose. Java code verbosity can also be partially attributed to the Java community's use of descriptiveness rather than conciseness as its standard. In this regard, the choice of different standards related to code formatting aesthetics is irrelevant. In addition, methods and sections of boilerplate code can be contained in methods that are integrated into the API. Without sacrificing accuracy or clarity, program code refactoring with an eye toward conciseness can greatly simplify redundant Java code. In some cases, Java's reputation for verbosity is a result of the large number of old code examples. Many books about Java were written many years ago. Since Java was already around when the entire World Wide Web first emerged, many online resources for Java provide code snippets from the earliest versions of the language. Over time, some visible problems and deficiencies have been fixed, and the Java language has matured, which means that even very accurate and well-implemented examples may not effectively take advantage of later language idioms and APIs. Java was designed to be object-oriented, easy to learn (at the time, that meant using C++-style syntax), robust, safe, portable, multithreaded, and high-performance. Simplicity was not one of them. Functional languages offer a much simpler alternative to tasks that can be accomplished with object-oriented syntax. The addition of Lambda expressions in Java 8 changed the way Java behaves, reducing the amount of code required to perform many common tasks and opening the door to functional programming idioms in Java. Functional ProgrammingFunctional programming treats functions as the core structure for program developers. Developers can use functions in a very flexible way, such as passing them as parameters. With this capability of Lambda expressions, Java can use functions as method parameters, or code as data. Lambda expressions can be thought of as anonymous methods that are not associated with any particular class. These concepts have a very colorful and fascinating mathematical basis. Functional programming and Lambda expressions are still relatively abstract and esoteric concepts. For developers, the main focus is on how to solve tasks in actual production, and they may not be interested in tracking the latest computing trends. With the introduction of Lambda expressions in Java, developers need to understand these new features at least to the extent that they can read the code written by other developers. These new features can also bring practical benefits - they can affect the design of concurrent systems and make them have better performance. This article is concerned with how to use these mechanisms to write concise and clear code. Lambda expressions can generate concise code for several reasons. The use of local variables is reduced, so the code for declaration and assignment is reduced. Loops are replaced with method calls, reducing more than three lines of code to one line. Code that would have been in nested loops and conditionals can now be placed in a single method. Fluent interfaces are implemented, and methods can be chained together in a manner similar to Unix pipes. The net effect of writing code in a functional style is not limited to readability. Such code avoids state maintenance and does not produce side effects. This code also has the added benefit of being easily parallelized, improving processing efficiency. Lambda ExpressionsThe syntax associated with lambda expressions is relatively simple and straightforward, but it is different from the idioms of previous versions of Java. A lambda expression consists of three parts: the parameter list, the arrow, and the body. The parameter list may or may not contain parentheses. In addition, there are new related operators consisting of double colons, which can further reduce the amount of code required for certain lambda expressions. This is also called a method reference. Thread CreationIn this example, a thread is created and run. The lambda expression appears on the right side of the assignment operator, specifies an empty parameter list, and simple message output is written to standard output while the thread runs.
Processing Collections One of the first places where developers notice the presence of Lambda expressions is in relation to the collection API. Suppose we need to sort a list of strings based on their length.
You can create a Lambda expression to achieve this function.
This example includes two parameters passed to the lambda expression body and compares the lengths of the two parameters.
There are many alternatives to operating on the elements of a list without using a standard "for" or "while" loop. Comparison semantics can also be accomplished by passing a lambda expression to the collection's "forEach" method. In this case, there is only one parameter passed in, and no parentheses are required.
This particular example can be further reduced by using method references to separate the containing class from the static method. Each element is passed to the println method in sequence.
java.util.stream is a new package introduced in Java 8, which processes collections with a syntax familiar to functional program developers. The package summary explains the contents of the package as follows: "Provides classes that provide support for functional operations on stream elements, such as map-reduce transformations on collections." The class diagram below provides an overview of the package, focusing on the functionality that will be used in the following examples. The package structure lists a large number of Builder classes. These classes, like fluent interfaces, can chain methods into a pipeline-like set of operations. Although string parsing and collection processing are simple, they still have many practical application scenarios in the real world. When doing natural language processing (NLP), sentences need to be segmented into individual words. Bioinformatics represents DNA and RNA as bases composed of letters, such as C, G, A, T, or U. In each problem domain, string objects are broken down and then manipulated, filtered, counted, and sorted on their individual components. Therefore, although the use cases included in the examples are very simple, the concepts are still applicable to a variety of practical tasks. The following example code parses a string object containing a sentence and counts the number of words and letters of interest. Including blank lines, the entire code listing is no more than 70 lines.
Sample program execution output: Sentence: the quick brown fox jumped over the lazy dog Words : 9 Letters : 44 Letters: t, h, e, , q, u, i, c, k, , b, r, o, w, n, , f, o, x, , j, u, m, p, e, d, , o, v, e, r, , t, h, e, , l, a, z, y, , d, o, g Sorted: , , , , , , , , a, b, c, d, d, e, e, e, e, f, g, h, h, i, j, k, l, m, n, o, o, o, o, p, q, r, r, t, t, u, u, v, w, x, y, z Unique: , a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, t, u, v, w, x, y, z Counts letters abcdefghijklmnopqrtuvwxyz 8 1 1 1 2 4 1 1 2 1 1 1 1 1 1 4 1 1 2 2 2 1 1 1 1 1 words quick | brown | jumped | over | lazy 5 | 5 | 6 | 4 | 4 The code above has been simplified several times. Some of these approaches are not available in every version of Java, and some of them may not conform to generally accepted coding style guidelines. Think about how you would have gotten the same output in earlier versions of Java. First, you would have created many local variables to temporarily store data or to act as indices. Second, you would have needed to tell Java what to do with the data through many conditional statements and loops. The new functional approach focuses on what data is needed, and doesn't worry about dealing with temporary variables, nested loops, index management, or conditional statements associated with it. In some cases, the adoption of standard Java syntax from earlier versions to reduce the amount of code comes at the expense of clarity. For example, the Java package in the standard import statement on line 1 of the sample code references all classes under java.util rather than referring to each class by name. The call to System.out.println is replaced with a call to a method named p, which allows the use of short names in each method call (lines 9-11). These changes are controversial because they may violate some Java coding standards, but programmers with other backgrounds may not have any problems viewing this code. In other cases, features that were only added in JDK8 Preview are utilized. Static references (lines 3-5) can reduce the number of classes that need to be referenced inline. Regular expressions (lines 10, 45) can effectively hide loops and conditionals in a way that has nothing to do with functional programming itself. These idioms, especially the use of regular expressions, are often criticized for being difficult to read and explain. If used properly, these idioms can reduce the amount of noise and limit the amount of code that developers need to read and explain. ***, the sample code uses the new Streaming API in JDK 8. A large number of methods in the Streaming API are used to filter, group, and process the list (lines 17-40). Although their association with the enclosed class is clear in the IDE, this relationship is not so obvious unless you are already familiar with these APIs. The following table shows the source of each method call that appears in the sample code.
The uniq() (line 13) and sort() (line 17) methods reflect the functionality of the Unix utilities of the same name. sort introduces a first call to the stream, first sorting it and then collecting the sorted results into a list. UniqueCount() (line 21) is similar to uniq -c, returning a map object where each key is a character and each value is a count of the number of times that character occurs. The two "getWords" methods (lines 26 and 33) are used to filter out words shorter than a given length. The getWordLengthsLongerThan() method calls some additional methods to format and convert the result into an unmodifiable String object. The entire code does not introduce any new concepts related to Lambda expressions. The syntax introduced previously is only applicable to specific usage scenarios of Java Stream API. SummarizeThe idea of using less code to achieve the same task is consistent with Einstein's idea: "It must be as concise and clear as possible, but not simpler." Lambda expressions and the new Stream API have attracted much attention for their ability to achieve concise code that scales well. They allow program developers to appropriately simplify the code into the best expression. Functional programming idioms are designed to be short, and if you think about it carefully, you will find many scenarios where Java code can be made more concise. The new syntax is a bit strange but not very complicated. These new features clearly show that Java as a language has far exceeded its original goals. It is open to accepting some of the best features of other programming languages and integrating them into Java. |
<<: Six tips for navigation bars
>>: If that were the case, Mr. Qiao would definitely be so angry that he would come back to life!
At the end of each year, many institutions organi...
"If a person's mobile phone number remai...
Mixed knowledge is the cure for confusion!...
Naturalist Pliny the Elder once said: On an opal,...
Why are we addicted to TikTok? Because we never k...
On October 12, 2024, Lei Zhanxu, Secretary Genera...
There is a popular saying on the Internet these d...
Today's Tadpole Quiz is here! The above is @A...
A correct marketing promotion process Marketing pr...
Product portfolio strategy is a strategy for ente...
According to relevant media reports, the Wall Str...
As we all know, Tik Tok is a video model and an i...
Traffic pool thinking is the guiding thinking for...
"Polar bears are left-handed" - this is...