Monday, December 27, 2021

11 great Java tricks from dev.java

The dev.java website is a huge resource for all Java developers. Here are 11 tricks collected from the site.

Download a PDF of this article

Trick #1: Compact record constructors

You know how to use records to model data carriers and how to verify incoming data during construction, but do you know that you don’t have to list the parameters of all the record constructors?

A record’s canonical constructor has one argument per component but in its compact form, you don’t have to list all the arguments. You can’t assign fields—that happens in compiler-generated code after yours—but you can reassign the parameters, which leads to the same result.

public record Range(int start, int end) {

    public Range {

        if (end <= start) {

            throw new IllegalArgumentException();

        }

    }

}

Trick #2: Serializing records

You know that every object can be serialized using black magic, but do you know that no such deviance is needed for records? The guaranteed presence of constructor parameters and accessors makes serialization work with the object model—and makes it easy for you to create reliable serializable records.

Trick #3: jpackage versus modules

You know jpackage, but wait: Do you really know jpackage?

jpackage is a command-line tool that takes an entire Java app as input and produces a fully self-contained application image, meaning it includes your code, dependencies, and a Java runtime. jpackage creates the runtime for your app with jlink, which you can fully configure through jpackage, or you can pass it the path to a runtime image that you already created.

Other configuration options for jpackage include the ability to specify application metadata such as icons and licenses, installation options, and launchers as well as JVM and program options.

jpackage produces output in platform-specific formats such as deb and rpm for Linux or exe and msi for Windows.

Now that you know jpackage, do you know that it can do all of that for modular as well as nonmodular applications?

# generate an application image for a modular application:

jpackage --type app-image -n name -p modulePath -m moduleName/className

# for a nonmodular application:

jpackage --type app-image -i inputDir -n name --main-class className --main-jar myJar.jar

Trick #4: Cross-OS runtime images

Speaking of jlink, do you know that you can use it to create runtime images across operating systems?

Say your build server runs Linux and you need a Windows runtime image. You simply need to download and unpack a Windows JDK of the same version as the Linux one that runs jlink, and then add the Windows jmods folder to the Linux jlink executable’s module path.

# download JDK for Windows and unpack into jdk-win

# create the image with the jlink binary from the system's JDK

# (in this example, Linux)

$ jlink

  --module-path jdk-win/jmods:mods

  --add-modules com.example.main

  --output app-image

Trick #5: Labeled breaks and continues

You know how to use the break statement to get out of an inner loop, but do you know that you can give the break a label to break out of an appropriately labeled outer loop as well? You can do likewise with continue, which skips the rest of the current iteration of the innermost loop, because if you pass it a label, it will skip the iteration of the labeled loop instead.

However, just because you can, doesn’t mean you should. Use this trick with care.

class ContinueWithLabelDemo {

    public static void main(String[] args) {

        String searchMe = "Look for a substring in me";

        String substring = "sub";

        boolean foundIt = false;

        int max = searchMe.length() -

                  substring.length();

    test:

        for (int i = 0; i <= max; i++) {

            int n = substring.length();

            int j = i;

            int k = 0;

            while (n-- != 0) {

                if (searchMe.charAt(j++) != substring.charAt(k++)) {

                    continue test;

                }

            }

            foundIt = true;

                break test;

        }

        System.out.println(foundIt ? "Found it" : "Didn't find it");

    }

}

Trick #6: Boolean expressions in pattern matching

You know pattern matching, but do you know that you can use the variable that pattern matching introduces in the same boolean expression?

For example, if you check whether the instance object is of type String with object instanceof String s, you can start using s straight away, such as to check whether s is nonempty with && !s.isEmpty(). This works in if statements in Java 16 and in switch as a preview in Java 17.

Object object = // ...

if (object instanceof String s && !s.isEmpty())

    System.out.println("Non-empty string");

else

    System.out.println("No string or empty.");

Trick #7: Generic wildcards and subtyping

You know generics and that a List<Integer> does not extend a List<Number>, as shown in Figure 1.

Java Trick, Oracle Java Certification, Oracle Java Tutorial and Materials, Oracle Java Prep, Java Preparation, Java Career, Oracle Java Learning
Figure 1. Generic inheritance

But do you know that if you add wildcards, you can create a type hierarchy, as shown in Figure 2? A List<? extends Integer> actually does extend a List<? extends Number>. And the other way around, a List<? super Number> extends a List<? super Integer>.

Java Trick, Oracle Java Certification, Oracle Java Tutorial and Materials, Oracle Java Prep, Java Preparation, Java Career, Oracle Java Learning
Figure 2. Generic inheritance with wildcards

Trick #8: Creating and chaining predicates


You know how to write lambdas to create predicates, but do you know that the interface offers many methods to create and combine them? Call the instance methods and, or, or negate for boolean formulas.

You have no predicates yet? No problem! The static factory method not is useful for inverting a method reference. And if you pass some object to isEqual, you create a predicate that checks instances for equality with that object.

Predicate<String> isEqualToDuke = Predicate.isEqual("Duke");
Predicate<Collection<String>> isEmpty = Collection::isEmpty;
Predicate<Collection<String>> isNotEmpty = Predicate.not(isEmpty);

Trick #9: Creating and chaining comparators


If you think that was cool, hold your breath for comparators: You know how to implement them, but do you know there are even more factory and combination methods?

To compare long, double, float, and other values, use a method reference to their static compare method.

Comparator<Integer> comparator = Integer::compare;

If you want to compare objects by one of their attributes, pass a function extracting it to the static method Comparator.comparing. To first sort by one and then another attribute, create both comparators, and then chain them with the instance method thenComparing.

Comparator<User> byFirstName = Comparator.comparing(User::getFirstName);
Comparator<User> byLastName = Comparator.comparing(User::getLastName);
Comparator<User> byName = byFirstName.thenComparing(byLastName);

Need a Comparator instance that uses a Comparable object’s compareTo method? The static factory method naturalOrder is there for you. If that’s the wrong way around, just call reversed on it.

Ah, and what to do about the pesky null? Worry not: Pass a Comparator to nullsFirst or nullsLast to create a Comparator that does what you need.

Comparator<Integer> natural = Comparator.naturalOrder();
Comparator<Integer> naturalNullsLast = Comparator.nullsLast(natural);

Trick #10: Executing source files as scripts


You know that you can use the java command to launch a single source file without having to manually compile the file first. But do you know that you can use this capability to write full-on scripts in Java in three simple steps?

First, add a shebang line to the source file that points at your java executable, followed by --source and the Java version the code was written for.

#!/path/to/your/bin/java --source 16

public class HelloJava {

    public static void main(String[] args) {
        System.out.println("Hello " + args[0]);
    }
}

Second, rename the file so it doesn’t end with .java. This is a great opportunity to give it a good command-line style of name.

Third, make the file executable with chmod +x. There you go: scripts in Java!

Trick #11: Loading jshell with all imports


You know how to launch jshell for some quick experiments but do you know that you don’t have to import everything manually? Simply launch jshell with the option JAVASE and all Java SE packages are imported, so you can get right to work.

jshell JAVASE

Source: oracle.com

Related Posts

0 comments:

Post a Comment