As of 2018, if you're still developing Java, you'll come across Eclipse projects that don't even use Ant quite often. If the automatic compilation of Eclipse passes, it is OK, the build is done manually based on the Excel procedure manual, and various jar files are thrown into the lib folder as well as the dependency library. Since there is no one who knows how to move it except on Eclipse, it is not possible to build from the command line, and CI conversion is a dream again.
This is a Java development modernization guide for those who want to break away from such Java development.
--Basically, it is supposed to be developed with Java 8 or later. --Assuming development on OpenJDK / OracleJDK. --Some may not be applicable for Android development. ――We will actively use English IDEs and tools. ――If you hate English, modernization is difficult. --Since it is premised on Java development in general, Web-specific ones are excluded. --Although it is described for Java, it can be applied to other languages as similar ones are prepared in most cases.
If you don't need Eclipse-specific plugins, use JetBrains' IntelliJ IDEA as your Java development environment. ** Community version can be used for business purposes without any problem **. (For details, see Article on commercial use of Community version)
https://www.jetbrains.com/idea/
--Easy to install on Windows / Mac / Linux --Various functions and plug-ins as IDE (plug-ins are inferior to Eclipse) --Maven, Gradle, Git, etc. are built into the IDE as standard --There are few problems around settings and plug-ins that you experience with Eclipse. --Main shortcuts and functions can be used in Android Studio, PyCharm, WebStorm, etc.
Eclipse has a lot of troubles around plugins and settings such as network proxy, so you should avoid it unless you have the necessary functions and plugins.
NOTE: If the situation around the plugin has improved dramatically, I would appreciate it if you could write in the comments.
Manage your project with Maven or Gradle. We recommend choosing Gradle because of its functionality and abundance of plugins.
NOTE: There are a certain number of people who find Maven easier to manage for large projects, so if you have a clear reason for that, choose Maven. Since Gradle is a Groovy-based DSL language, the project file build.gradle tends to be chaotic.
--Automatic download and installation of Gradle itself by Gradle Wrapper --No need to install Gradle itself in advance --Place and manage Gradle build.gradle for each project --Can describe build, test, deploy, etc. (often in time by default) --Java / Kotlin coexistence project settings, dependency downloads, etc. are all done automatically. --Dependent libraries are automatically downloaded --Abundant plugins --Various tasks such as code quality check, build, and release can be automated. --Good compatibility with IntelliJ IDEA --Automatically builds projects such as folder structure and dependent libraries
IDE-specific settings (operation when saving, coding format, etc.) should use the project-related files of Eclipse and IntelliJ IDEA, but other builds and CI surroundings should basically be Gradle-based.
Use Code Formatter to automatically apply coding conventions. As for the coding standard, we recommend Google Java Style
, which also supports new language specifications such as Java 8 lambda expressions.
https://google.github.io/styleguide/javaguide.html
If you want to format the code based on the coding standard when you save the source code in IntelliJ, it is recommended to combine the following plug-ins.
Reformat Code
, ʻOptimize Imports`, etc. when saving.
Reformat Code
is executed.
You can check whether the coding standard is followed by using Check Style
described later.
There are various Lint / static analysis tools in Java, but from the viewpoint of cost effectiveness, it is recommended to include the following two first. ** Gives a firmer point than intermediate Java developers. ** Face-to-face code review should be done from the perspective that these static analysis tools do not handle (ex. Implementation as designed or whether it conforms to principles such as DRY / SLAP).
If you want to further strengthen the check, add PMD and Check Style to the above two. I recommend it. However, the priority may be slightly lower for the following reasons.
--Compared to ʻError-Prone and
SpotBugs + fb-contrib`, PMD checks and points out quite fine details. Therefore, the cost-effectiveness for renovation may be rather low. However, there are some useful things such as cyclomatic complexity check, so it is better to narrow down the check rules.
--If you have already applied the Code Formatter mentioned above, there are few points to be pointed out by Check Style. However, it is necessary if you want to perform automatic check with CI or JavaDoc syntax check.
There are other tools such as the following, so let's actively use them as needed.
With the version upgrade of Java, useful language functions are gradually increasing. By utilizing these language functions, various processes can be described more concisely and robustly, so let's actively utilize them.
By the way, ʻError-Prone and
SpotBugs + fb-contrib` mentioned above also point out that it is recommended to use some of the new features of Java 7/8 introduced here.
Conditional branching of String can be described by switch syntax instead of if / else.
switch (s) {
case "Taro":
System.out.println("Taro");
break;
case "Hanako":
System.out.println("Hanako");
break;
}
By the way, the complexity of the switch syntax of String is O (1), so the performance is better than the complexity of if / else O (N). For details, refer to the URL below.
How is String in switch statement more efficient than corresponding if-else statement?
In conventional Java, it was necessary to describe the close process in the finally clause in order to avoid omission of close of streams.
However, in Java 7 and later, the following try-with-resources syntax is used to guarantee that the close process is executed without writing the close process in the finally clause. It's like the with
syntax in Python.
try (FileReader fr = new FileReader(path)) {
fr.readLine();
}
By the way, it is also possible to describe multiple Reader / Writer etc. by separating the inside of try (...)
with a semicolon. Of course, everything described here will be closed automatically at the end.
try (FileReader fr = new FileReader(inPath);
FileWriter fw = new FileWriter(outPath)) {
//Describe the process
}
NOTE: Kotlin has a similar Sequence, which makes it even more convenient.
When turning a collection with a for statement, I think that it is often the case that an element that meets a certain condition is simply extracted or converted to another element. For such use cases, use a Declarative implementation with the Stream API
instead of a **` imperative implementation with a for statement **.
The following is an example of implementing the process of "finding the longest number of characters in a word containing a" with both the for loop and Stream API. It may seem strange to those who are new to it, but I think it's easier to understand how the Stream API handles collections.
List<String> words = Arrays.asList("apple", "taro", "longest");
//for loop(Imperative)
int maxLength = -1;
for(String word : words) {
if (word.contains("a")) {
if (word.length() > maxLength ) {
maxLength = word.length();
}
}
}
//For Stream API(Declarative)
OptionalInt maxLength = words.stream()
.filter(s -> s.contains("a"))
.mapToInt(String::length)
.max();
Here, in order to give you a better sense of the strength of the Stream API, let's add "only for odd-numbered words" to the character count condition in the above example. If you add the condition, the code will be as follows. At this point, the for loop is becoming a bit overwhelming, but the Stream API maintains a well-controlled implementation. I won't write any more, but if you add three or four conditions, the difference will be even more pronounced. In the case of Stream API, you can easily "change max () to min () because you want to find the minimum value" or "change max () to average () because you want to find the average".
List<String> words = Arrays.asList("apple", "taro", "longest");
//for loop(Imperative)
int maxLength = -1;
for(String word : words) {
if (word.contains("a")) {
// ***Added even check AND condition***
if (word.length() > maxLength && word.length() % 2 == 0) {
maxLength = word.length();
}
}
}
//For Stream API(Declarative)
OptionalInt maxLength = words.stream()
.filter(s -> s.contains("a"))
.mapToInt(String::length)
.filter(n -> n % 2 == 0) // ***Added even check filter***
.max();
Among the functions added in Java8, Stream API is one of the very important functions that has the feature that "because it is declarative, it is easy to add or change processing, and it is easy to convey the intention of implementation". It is recommended to actively replace the parts that are replaced by the existing for loop processing.
By the way, IntelliJ is very convenient because it automatically completes the arguments of Stream API map
, filter
and collect
.
NOTE: Kotlin is even more convenient and you can do a lot of Ruby-ish things
In the era before Java8, it was necessary to prepare an anonymous class when defining a process such as Runnable, but in Java8 and later, such an ad hoc anonymous class can be eliminated by a lambda expression.
//For anonymous classes
Runnable runner = new Runnable() {
public void run() {
System.out.println("Do something...");
}
};
//For lambda expression
Runnable runner = () -> {System.out.println("Do something...");};
This alone may not seem like a big advantage. However, in asynchronous processing programs and Android development, which tend to create a large number of anonymous classes, the description becomes simple and the readability is greatly improved, which is effective in reducing maintenance costs later.
In addition, the Stream API explained in the previous item also uses a lambda expression for the filter method etc., which is useful when performing a little functional programming in Java.
In IntelliJ, if you try to complete a lambda expression where it can be entered, the appropriate lambda expression completion candidates (appropriate number of arguments, function blocks, etc.) will be displayed. Similar to the Stream API, it's very convenient.
NOTE: Lombok is almost unnecessary when using Kotlin, but if you want something like Kotlin's data class in Java, use Lombok.
Lombok is a Java library that automatically generates Java constructors, getters, setters, etc. just by adding annotations.
If you want to create Setter / Getter, default constructor, and all constructors many times when creating a class for data in Java, it is recommended to use Lombok. In particular, when defining a data access related class etc. to use a DB related framework etc., you can declaratively define the class by using Lombok.
IntelliJ and Eclipse also provide plugins that allow you to properly complete each method (Setter, Getter, constructor, etc.) automatically generated by the Lombok annotation, and even check the compilation. Therefore, even if you are using Lombok, static checks on the IDE will be done properly.
Make IntelliJ compatible with Lombok
If you are comfortable with using Kotlin in your product code, it is also recommended to create a data class etc. in Kotlin. Kotlin will be discussed later.
Guava is a Java core library made by Google.
We have a complete set of useful libraries such as various collections, Immutable collections, functional types, I / O, string processing, and parallel processing utilities.
NOTE: Compared to Apache Commons, Guava has many more modern and higher level generalized libraries. Apache Commons has more practical libraries such as email, CLI, Net, Logging, and DB, so the usages do not completely overlap.
ʻError-Prone` introduced in other items also points out immutability etc., and at this time Guava recommends the use of Immutable related collections. So even if you don't know how to use Guava in your product code, it's a good idea to have Guava installed for more robust code.
Kotlin is a JVM language developed by JetBrains. It has the same performance as Java and is fully interoperable with Java. When using Kotlin from Java, it is necessary to annotate Kotlin with @JvmStatic
etc., but when using Java from Kotlin, it can be used as if it were a Kotlin library. The main features are listed below.
--Null can be eliminated --Performance almost equivalent to Java (Groovy, Scala, etc. basically have lower performance) --Variable embedding in a string using a template string --A lot of string operations, file / stream operations, and collection operations --Redundant description of set / get can be eliminated by data class and property --You can set default arguments for the method --You can specify the argument name when calling the method. --Supports async / await for parallel processing by coroutines
One of the big advantages of using Kotlin is that it is easy to write code short and concisely because you can use convenient functions and syntax like Python and Ruby while keeping Java static typing * *I think. With Java, implementation and maintenance are difficult without using various libraries and frameworks, and Kotlin can often easily write processing that makes you think "Python or Ruby can be written concisely". In addition, some design patterns (ex. Singleton, Builder) are not required in Kotlin in the first place, so the design may be simpler than Java.
NOTE: Kotlin also adds keywords (ex. Data class, open, sealed, lateinit, override, internal, object, companion object) that allow you to more clearly describe relationships such as class interfaces and inheritance. This has the advantage of being more clearly designed and implemented. If you're involved in large-scale development, this advantage may seem more appealing.
Java has improved a lot, especially since Java 8, but Kotlin is still years ahead of Better Java. The new Java 7/8 features and features such as the Lombok library introduced so far are almost unnecessary because they are supported by Kotlin itself.
NOTE: Let's take a look at method completion candidates for instances such as String, Integer, File, InputStream, and OutputStream in Kotlin. I'm a little impressed with "Is there something like this!"
By using IntelliJ and Gradle, you can use both Java and Kotlin source code in the same project. For this reason, in Java, it is possible to implement Kotlin only for implementations that make the description very verbose or complicated, and apply Kotlin step by step.
One thing to keep in mind is the fact that Kotlin is unlikely to continue to be the mainstream language beyond Java in the OpenJDK / Oracle JDK JVM environment for the following reasons: For this reason, some people may find it difficult to introduce it into the product code due to problems such as development personnel. In that case, it is recommended to implement test code and tools in Kotlin.
--There is no official support for Oracle. (Supplement: Kotlin is the official Google supported language on Android) --There is no killer content library that can be used only with Kotlin. --Basically, you can access Kotlin libraries etc. from Java. ――In the case of "switching from Java to Kotlin", there is not necessarily a big merit like "switching from C to Java". --Ex. Abundant development tools such as multi-platform support, memory management, IDE, etc. --There is a possibility that existing development tools and development standards for Java cannot be used.
NOTE: Kotlin is officially supported by Google on Android. Therefore, it is quite possible that Kotlin will become the mainstream language beyond Java for Android.
The test using Kotlin is described in the test section.
In the Gradle project, Java and Kotlin can be used properly in the product code (under src / main) and the test code (under src / test). Therefore, you can proceed with development in the form of ** "Implement product code in Java and test code in Kotlin" **.
By implementing your test code in Kotlin, you can benefit from the following:
--Data class makes it easy to convert test conditions into data --You can also specify default values, so you can easily define default values for test data. --Template strings make test case strings easier --JSON that just replaces some variables eliminates the need for external files and template engines --Default arguments make it easy to extend and manage test case classes and methods --No need to add overload like Java, and less Builder pattern --Collection operations, file operations, etc. become easier --It can be realized to some extent even with Java by using Guava and Apache Commons.
In 2017, JUnit5 was officially released. It is convenient due to the expansion of basic functions, support for Java 8 functions, and a design that can be extended by plug-ins.
--It is now possible to describe the processing before and after @BeforeEach
, @Test
, @AfterEach
, etc.
--In JUnit4, it was possible to describe to some extent by Rule, but it was not possible to describe with such fine particle size.
--Supports parameter tests, etc. (However, it is necessary to pull in the Java library of the plug-in)
--Gradle officially supports JUnit 5 with v4.6 or later
--By using JUnit Vintage, it is possible to coexist with JUnit 4, so test cases can be migrated in stages.
NOTE: Try not to implement or test a lot of mock. Testing that relies on a large number of mock tends to be less cost-effective and is likely to manifest itself as a large technical debt when adding or changing features. (Reference: Summary article on lean test)
Basically, use Mockito (Version 2) as a Java mock tool.
There is also a mock library called JMockit, which has more powerful functions around the mock, but APIs that have become deprecated due to many API specification changes will be deleted as soon as you are alert. In the world of Java, where there are many libraries that emphasize backward compatibility, it is quite like a fan. Therefore, it may not be suitable for those who want to regularly upgrade and benefit from the latest bug fixes and feature additions. Avoid it unless you have a strong reason to say, "This feature of JMockit is essential for mocking." Even if you adopt it, upgrade it carefully.
Below is a simple example of Mockito. It is a form of defining a mock by method chaining that is common in Java these days. In addition to this, it is also possible to describe by specifying annotations such as @ Mock
, so select the appropriate one.
Mockito
import static org.mockito.Mockito.*;
//Prepare a mock
LinkedList mockedList = mock(LinkedList.class);
when(mockedList.get(0))
.thenReturn("first");
//Use mock
System.out.println(mockedList.get(0));
It seems that Java has many bad images such as "obsolete" or "slow", probably because it has reigned at the top for a long time mainly in the Japanese SIer industry. But Java is still one of the most powerful programming languages.
Java is one of the most powerful platforms around the JVM virtual machine and strong ecosystem. JVM (Java Virtual Machine) is a fast, robust and highly functional execution environment, and various JVM languages such as Groovy / Scala / Kotlin / Clojure can be used. Many of the various libraries and frameworks are abundant and highly complete, and the development tools such as IDEs are also outstanding. Above all, the merit that ordinary developers can use its track record and reliability, which is described as Battle Tested
, is immeasurable.
I mainly use Python for small program and tool development, but I often choose Java for systems where performance and stability are important. C / C ++, Golang, etc. may be options when extremely high efficiency and performance are required, but in many cases the Java platform can achieve the goal.
It's such a nice Java, but it has a very bad reputation on the Internet. Probably due to the following by-products of the Java Dark Ages:
--I've been touching the ugly enterprise elements of Java --XML hell such as J2EE framework --Meaningless or excessive design patterns --SIer's original framework that further boiled down the above and condensed --Has only been developed by a low level development team --Stopped in development in the Java 1.4 era (in the era without generics) --I don't even use Ant, let alone Gradle / Maven --Reinventing the low quality wheel --Development that does not know the command line and relies on the functions provided by the IDE
By the way, at the development sites of major Japanese companies, it is not uncommon for projects to fall under these categories. This doesn't improve Java's reputation.
But in reality, the Java platform and development environment are steadily evolving.
--Evolution of JVM (Parallel Full GC for G1, Docker support, etc.) --Introduction of JVM language that can interoperate with Java (Groovy, Scala, Clojure, Kotlin, etc.) --Java language evolution (Stream API, lambda expressions, type inference, etc.) --Introduction of new development model due to the rise of Android development (RxJava, Kotlin, etc.) --Evolution of development and build tools (IntelliJ, Maven, Gradle, etc.) --Evolution of Web development (Spring Boot, Play, Grails, Dropwizard, etc.) --Evolution of development libraries (Guava, Guice, Lombok, etc.) --Evolution of asynchronous program development (Akka, Netty, Vert.x, etc.) --Evolution of test library (JUnit5, Mockito2, Spock, Cucumber, etc.)
These newly emerging forces are often capable of more modern development by eliminating problems with Java and frameworks (excessive design patterns, XML hell). Perhaps if you are a beginner to intermediate Java developer, most of your complaints will be resolved with ʻIntelliJ + Gradle + Kotlin`.
TODO
Since the whole is described broadly and shallowly, the following topics will be added and reinforced when there is time.
--Example of build.gradle description when using various tools, how to integrate --Since the test system is almost only a conclusion, add the functions, merits, etc. around the test tool. --Post the reference site URL for each topic
Recommended Posts