Friday, December 16, 2022

Quiz yourself: Sealed and non-sealed classes and interfaces

Oracle Java, Java Certification, Java Career, Oracle Java Preparation, Java Tutorial and Material, Oracle Java Tutorial and Material


Given the following code

sealed interface Super permits Sub1, Sub2, Sub3 {}
non-sealed class Sub1 implements Super {}
record Sub2(Super s) implements Super {}
enum Sub3 implements Super { SUB_A{}, SUB_B{} }

Which statement is true? Choose one.

A. The code compiles as it is.
B. The record Sub2 is invalid.
C. The enum Sub3 is invalid.
D. Both record Sub2 and enum Sub3 are invalid.

Answer. The purpose of sealed types is to let you limit the set of types that can be assignment-compatible with a particular base type.

A common example of inheritance would be a base type Animal with child classes for Lion, Tiger, Bear, and so on. This structure makes perfect sense if you decide to add Snake, Dog, Bird, and any number of additional types that are subtypes of Animal.

Sometimes, however, you might need to model a situation where there are a limited number of possible or permissible subtypes. An example would be a base type Quadrilateral (four-sided geometric shape). The possible subtypes of this would be Square, Rectangle, Parallelogram, Trapezium, Trapezoid, Rhombus, and Kite. (Let’s not get picky about the geometric definitions, as we recall that two of those have different meanings on opposite sides of the Atlantic Ocean!)

There are good reasons (some of which are related to the form of switch/case construction that’s a preview feature in Java 17 and Java 18) why it can be helpful to the integrity of the design to be able to enforce this restriction and not allow any additional subtypes. It’s beyond this article’s scope to discuss those reasons, but it’s sufficient to know that the sealed class syntax is provided to allow you to enforce this.

To make the sealed class syntax work, the syntax must allow you to define a base type (which can be an interface, abstract class, or concrete class) and enumerate the permitted subtypes. Further, the syntax must enforce controls on the subtypes of those subtypes. This goal suggests the following rules:

◉ A sealed type must (generally—but this article will not get into the exception) explicitly declare the permitted subtypes.
◉ Each permitted subtype must either be sealed or final (in which case it cannot have any subtypes).
◉ It’s also permitted to have a subtype that’s explicitly declared as non-sealed, in which case it allows any number of subtypes.

All sealed classes have some interactions with existing type rules. For example, abstract classes and interfaces cannot be final, so that possibility is removed and either of them can be sealed or non-sealed, but they cannot be final.

You can probably guess that enums, which prohibit arbitrary subclassing, and record types, which are implicitly final, also interact with these rules. That will be explored shortly.

Now that you’ve got a good starting point, consider the elements presented in this question.

The declaration of Super is valid. An interface can be sealed, the syntax is good, and the declaration enumerates the three permitted subtypes: Sub1, Sub2, and Sub3.

The declaration of the class Sub1 is also valid. It demonstrates the use of the non-sealed idea. It’s permitted to have arbitrary subtypes, and it’s explicit about that. Thus it is correct.

The record Sub2 declaration is also valid. It’s not labeled final, sealed, or non-sealed, but a record is implicitly final, so it’s acceptable in that respect. Also, although a record cannot declare a parent class, it’s fine for a record to implement an interface, as this one does. You could add the final modifier explicitly, but that is not required, so the code for Sub2 compiles.

The Sub3 enum is interesting. Unlike the record Sub1, an enum is not necessarily final. It’s true that you can’t use extends from an enum, but you can create subclasses if you do so inside the enum itself—and the code in the question does just that.

Both the constant values SUB_A and SUB_B are followed with curly brace pairs, and this means that each instance is a subclass of the Sub3 enum. Because the subtypes of an enum are so tightly controlled, the Java Language Specification treats an enum that contains children in this way as being implicitly sealed. Section 8.9 of the specification for Java 17 says

An enum class is either implicitly final or implicitly sealed, as follows:

◉ An enum class is implicitly final if its declaration contains no enum constants that have a class body.
◉ An enum class E is implicitly sealed if its declaration contains at least one enum constant that has a class body. The permitted direct subclasses of E are the anonymous classes implicitly declared by the enum constants that have a class body.

In the situation where no class bodies were provided for any of the individual enum constants, the enum class would be implicitly final and would work too.

Because the Sub1 class is explicitly marked non-sealed, the record Sub2 is implicitly final, and the enum Sub3 is implicitly sealed, all three are valid and the syntax of the code presented is entirely correct and compiles. In view of that, you can see that option A is correct, and therefore options B, C, and D must be incorrect.

Conclusion. The correct answer is option A.

Source: oracle.com

Wednesday, December 14, 2022

Nothing is better than the Optional type. Really. Nothing is better.


JDK 8 introduced the Optional class, a container that is either empty or contains a non-null value.

Core Java, Java Exam, Java Exam Prep, Java Tutorial and Materials, Java Guides, Java Skills, Java Jobs

Optional has numerous problems without countervailing benefits. It does not make your code more correct or robust. There is a real problem that Optional tries to solve, and this article shows a better way to solve it. Therefore, you are better off using a regular and possibly null Java reference rather than Optional.

The web and blogosphere are full of claims that the Optional class solves the problem of null pointer exceptions. This is not true. Changing your code to use the Optional class has the following negative effects:

◉ Optional transforms a NullPointerException into a NoSuchElementException, which still crashes your program.
◉ Optional creates new problems that were not a danger before.
◉ Optional clutters your code.
◉ Optional adds space, time, and coding overhead.

When your code throws a NullPointerException or NoSuchElementException, the underlying logic bug is that you forgot to check all possibilities when processing data. It’s best to use a tool that guarantees you don’t forget. That helps you to understand and fix the underlying problem.

(These criticisms aren’t specific to Java’s implementation of Optional. Other languages that claim to have solved the null pointer problem have also merely transformed it into a different manifestation.)

To be clear, the Optional class isn’t all bad: If you need to use Optional, it defines methods for reducing code clutter when dealing with possibly present data. However, you should still avoid the Optional class.

The rest of this article expands on the points raised above.

Changing the exception that is thrown doesn’t fix the defect or improve the code


Consider the following Cartesian point class with fields x and y (this discussion is equally applicable to getter methods):

class Point { int x; int y; }

Because a Java reference may be null, there is a danger of a NullPointerException whenever you dereference it, as in myPoint.x below.

Point myPoint;
...
... myPoint.x ...

If myPoint is null and does not refer to a real point, then myPoint.x throws a NullPointerException and the program crashes. The following is a way to write this code using Optional:

Point myPoint;
Optional<Point> myOPoint = Optional.ofNullable(myPoint);
...
... myOPoint.get().x ...

If myOPoint does not contain a real point, then myOPoint.get().x throws a NoSuchElementException and the program crashes. This isn’t any better than the original code, because the programmer’s goal is to prevent all crashes, not just NullPointerException crashes!

It is possible to prevent the exception and crash by doing a check first.

if (myPoint != null) {
  ... myPoint.x ...
}

or

if (myOPoint.isPresent()) {
  ... myOPoint.get().x ...
}

Again, the code is very similar and Optional is not superior to using a regular Java reference.

Optional is prone to misuse


Optional is a Java class; therefore, the variable myOPoint of type Optional<Point> might be null. Thus, the expression myOPoint.get() might throw a NullPointerException or a NoSuchElementException! You really need to write the following:

if (myOPoint != null && myOPoint.isPresent()) {
  ... myOPoint.get().x ...
}

You can express complex data using the distinction between a null Optional value, a non-null Optional with data absent, and a non-null Optional with data present, but this is complex and confusing. Alternatively, you could decide to forgo those possibilities and to be careful and disciplined about not letting variables of type Optional be null. However, if you trusted yourself about that, you wouldn’t have had any null pointer exceptions in the first place and you wouldn’t be using Optional.

Optional is a wrapper, so uses of value-sensitive operations are error-prone, including reference equality checks (==), identity hash codes, and synchronization. You need to remember to not use these.

This isn’t a new issue: Back in 2016, Stuart Marks provided a longer list of rules to avoid mistakes in the use of Optional.

Optional clutters your code


With the Optional library, your code is more verbose, as shown in the following examples:

◉ Code for type names: Optional<Point> versus Point
◉ Code for checking a value: myOPoint.isPresent() versus myPoint == null
◉ Code for accessing data: myOPoint.get().x versus myPoint.x

None of these is a deal-breaker alone, but overall, Optional is cumbersome and ugly to use. For some concrete examples, see the Code Project blog’s “Why we should love ‘null’” post and search for the word cumbersome.

Optional introduces overhead


Optional introduces space overhead: An Optional is a separate object that consumes extra memory.

Optional introduces time overhead: Its data must be accessed via an extra indirection, and calling methods on it is more expensive than Java’s efficient test for null.

Optional introduces coding overhead: You must deal with Optional’s incompatibility with existing interfaces that use null, with the fact that it is not serializable, and so on.

The real problem: Remembering to perform checks


A NullPointerException or NoSuchElementException occurs because the programmer forgot to perform a check to see if data is present, via != null or .isPresent(), before trying to use the data.

Many people say that the main benefit of Optional is that with Optional, you are less likely to forget to perform the check. If true, that is good! Nonetheless, it’s not enough to make a problem somewhat less likely, in the few places where Optional is written. It is better to have a guarantee that eliminates the problem everywhere.

One way would be to force the programmer to always perform the check before accessing the data. (This is what some programming languages do, by offering a destructuring or pattern-match operator.) This would result in many redundant checks in places where the check has already been performed or it is not really needed. (As an analogy, think about how some programmers react to checked exceptions, which force the programmer to do a check whether the programmer wants to do it or not.)

A better approach is to have a tool that guarantees that you do not forget to check but that also doesn’t require redundant checks. Luckily, such tools exist; examples include Nullness Checker of the Checker Framework, NullAway, and Infer. (Note: I am the creator of the Checker Framework.)

As an example, consider Nullness Checker. It works at compile time, and it examines every dereference in your program and requires that the receiver is known to be non-null. That could be because you have already checked it or because it was generated by a source that never produces null.

Nullness Checker uses powerful analysis to keep track of whether a reference might be null. By comparison to the use of Optional, this reduces the number of warnings and the number of redundant checks that are needed. By default, Nullness Checker assumes that references are non-null, but you can specify possibly missing data by writing @Nullable, as in the type @Nullable Point.

Writing @Nullable Point is analogous to Optional<Point>, but with the following significant benefits:

◉ There is less clutter, because you write the @Nullable annotation on fields and method signatures—typically not within method bodies.
◉ It is compatible with existing Java code and libraries. There is no need to change your code to call methods of Optional. There is no need to change interfaces and clients to use the Optional type and no need to convert between Optional instances and regular references.
◉ There is no runtime overhead.
◉ You get a compile-time guarantee or a warning, never a runtime crash.
◉ The code is better documented. If Optional is not present on a type, you don’t know whether the programmer forgot it, Optional could not be written because of backward compatibility, or the data is really always present. With the static analysis of Nullness Checker, the annotations are machine-checked at compile time, so the program has @Nullable on every reference that might be null.
◉ You get guarantees about sources of null pointer exceptions, such as partially initialized objects and calls to Map.get, that Optional is not applicable to. It can also express method preconditions, which are useful for fields containing possibly missing data.

Nullness Checker achieves the goal of guaranteeing that you never forget to check for the presence of data, anywhere in your code, in a way that is less disruptive than the use of Optional. Other tools such as NullAway and Infer give similar guarantees with different trade-offs.

Since every programmer error related to null references is possible with Optional, and Optional makes new types of errors possible, programmers need support to avoid making all those errors. The Checker Framework also contains a compile-time Optional Checker that does exactly that, and it is useful if you need to use Optional (such as to interface with a library that uses Optional).

Optional’s handy methods


Although Optional tends to clutter your code, if you need to use Optional, it provides methods that reduce the clutter. Here are two examples.

◉ Its orElse method returns the value if it is present, or else it returns a default value.
◉ Its map method abstracts the pattern by doing the following:
  ◉ It takes as input a value.
  ◉ If the value is null, it returns null.
  ◉ Otherwise, it applies a function to the value and returns the result.

There are libraries that do the exact same things for regular Java references. An example is the Opt class that is distributed with the Checker Framework. For each instance method in Optional, the Opt class includes a static method.

Other methods such as filter and flatMap are described in the API documentation for Optional. These eliminate much of the need for calling Optional.isPresent() and Optional.get(), which is a great benefit. However, they don’t eliminate all the need, and the other disadvantages of Optional remain.

Counterarguments


Not everyone claims that Optional solves the problem underlying null pointer exceptions. For instance, Oracle’s JDK team does not claim this.

A general programming rule is to avoid, as much as possible, the situation that data is not present. This reduces the need to write a type such as Optional<Point> or @Nullable Point. All the arguments in this article continue to hold wherever in your program data might not be present.

Some people suggest (see Stuart Marks’ presentation on bikesheds) that programmers should use Optional sparingly, such as only on method return types and never on fields. If you use Optional less, there is less clutter, overhead, and potential for misuse. However, the only way to eliminate Optional’s problems is to not use it. Furthermore, if you use Optional less, you obtain fewer of its benefits. Null pointer exceptions are important no matter what their source or syntactic form, so the best solution is one that handles every reference in your program, not just some of them.

The main argument for Optional on return types is, “It’s too easy for clients to forget to handle the possibility of a null return value. Optional is ugly and in your face, so a client is less likely to forget to handle the possibility of an empty return value. Everywhere else, programmers should continue to use normal references, which might be null.” By contrast, my suggestion is that programmers should continue to use normal references everywhere in their program but use a tool to ensure that at every possible location—not just at method calls—the program checks against null when needed.

If you find a style of using Optional that solves part of your problems, at an acceptable cost to you, then good for you—use it.

Source: oracle.com

Monday, December 12, 2022

Efficient JSON serialization with Jackson and Java


When you’re building distributed systems in Java, the problem of serialization naturally arises. Briefly, serialization is the act of creating a representation of an object to store or transmit it and then reconstruct the same object in a different context.

Oracle Java Certification, Oracle Java Career, Java Jobs, Java Prep, Java Tutorial and Materials, Java Learning, Oralce Java JSON

That context could be

◉ Needing the same object in the same JVM but at a different time
◉ Needing the same object in a different JVM, which might be on a different machine
◉ Needing the same object in a non-JVM application

The last of these possibilities deserves a bit more thought. On the one hand, working with a non-JVM application opens the possibility of sharing objects with the whole world of network-connected applications. On the other hand, it can be hard to understand what is meant by “same object” when the object is reconstituted in something that isn’t a JVM.

Java has a built-in serialization mechanism that is likely to have been partially responsible for some of Java’s early success. However, the design of this mechanism is today viewed as seriously deficient, as Brian Goetz wrote in this 2019 post, “Towards better serialization.” While the JDK team has researched ways to rehabilitate (or maybe just remove) the inbuilt platform-level serialization in future versions of Java, developers’ needs to serialize and transport objects have not gone away.

In modern Java applications, serialization is usually performed using an external library as an explicitly application-level concern, with the result being a document encoded in a widely deployed serialization format. The serialization document, of course, can be stored, retrieved, shared, and archived. A preferred format was, once upon a time, XML; in recent years, JavaScript Object Notation (JSON) has become a more popular choice.

Why you should serialize in JSON


JSON is an attractive choice for a serialization format. The following are some of the reasons:

◉ JSON is extremely simple.
◉ JSON is human-readable.
◉ JSON libraries exist for nearly every programming language.

These benefits are counterbalanced by some negatives; the biggest is that a document serialized by JSON can be quite large, which can contribute to poor performance for larger messages. Note, however, that XML can create even larger documents.

Also, JSON and Java evolved from very different programming traditions. JSON provides for a very restricted set of possible value types.

◉ Boolean
◉ Number
◉ String
◉ Array
◉ Object
◉ null

Of these, JSON’s Boolean, String, and null map fairly closely to Java’s conception of boolean, String, and null, respectively. Number is essentially Java’s double with some corner cases. Array can be thought of as essentially a Java List or ArrayList with some differences.

(The inability of JSON and JavaScript to express an integer type that corresponds to int or long turns out to cause its own headaches for JavaScript developers.)

The JSON Object, on the other hand, is problematic for Java developers due to a fundamental difference in the way that JavaScript approaches object-oriented programming (OOP) compared to how Java approaches OOP.

A class comparison. JavaScript does not natively support classes. Instead, it simulates class-like inheritance using functions. The recently added class keyword in JavaScript is effectively syntactic sugar; it offers a convenient declarative form for JavaScript classes, but the JavaScript class does not have the same semantics as Java classes.

Java’s approach to OOP treats class files as metadata to describe the fields and methods present on objects of the corresponding type. This description is completely prescriptive, as all objects of a given class type have exactly the same set of methods and fields.

Therefore, Java does not permit you to dynamically add a field or a method to a single object at runtime. If you want to define a subset of objects that have extra fields or methods, you must declare a subclass. JavaScript has no such restrictions: Methods or fields can be freely added to individual objects at any time.

JavaScript’s dynamic free-form nature is at the heart of the differences between the object models of the two languages: JavaScript’s conception of an object is most similar to that of a Map<String, Object> in Java. It is important to recognize that the type of the JavaScript value here is Object and not ?, because JavaScript objects are heterogeneous, meaning their values can have a substructure and can be of Array or Object types in their own right.

To help you navigate these difficulties, and automatically bridge the gap between Java’s static view and JavaScript’s dynamic view of the world, several libraries and projects have been developed. Their primary purpose is to handle the serialization and deserialization of Java objects to and from documents in a JSON format. In the rest of this article, I’ll focus on one of the most popular choices: Jackson.

Introducing Jackson


Jackson was first formally released in May 2009 and aims to satisfy the three major constraints of being fast, correct, and lightweight. Jackson is a mature and stable library that provides multiple different approaches to working with JSON, including using annotations for some simple use cases.

Jackson provides three core modules.

◉ Streaming (jackson-core) defines a low-level streaming API and includes JSON-specific implementations.
◉ Annotations (jackson-annotations) contains standard Jackson annotations.
◉ Databind (jackson-databind) implements data binding and object serialization.

Adding the databind module to a project also adds the streaming and annotation modules as transitive dependencies.

The examples to follow will focus on these core modules; there are also many extensions and tools for working with Jackson, which won’t be covered here.

Example 1: Simple serialization


The following code fragment from a university’s information system has a very simple class for the people in the system:

public class Person {
    private final String firstName;
    private final String lastName;
    private final int age;

    public Person(String firstName, String lastName, int age) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public int getAge() {
        return age;
    }
}

Jackson can be used to automatically serialize this class to JSON so that it can, for example, be sent over the network to another service that may or may not be implemented in Java and that can receive JSON-formatted data.

You can set up this serialization with a very simple bit of code, as follows:

var grant = new Person("Grant", "Hughes", 19);

var mapper = new ObjectMapper();
try {
    var json = mapper.writeValueAsString(grant);
    System.out.println(json);
} catch (JsonProcessingException e) {
    e.printStackTrace();
}

This code produces the following simple output:

{"firstName":"Grant","lastName":"Hughes","age":19}

The key to this code is the Jackson ObjectMapper class. This class has two minor wrinkles that you should know about.

◉ Jackson 2 supports Java 7 as the baseline version.
◉ ObjectMapper expects getter (and setter, for deserialization) methods for all fields.

The first point is not immediately relevant (it will be in the next example, which is why I’m calling it out now), but the second could represent a design constraint for designing the classes, because you may not want to have getter methods that obey the JavaBeans conventions.

It is possible to control various aspects of the serialization (or deserialization) process by enabling specific features on the ObjectMapper. For example, you could activate the indentation feature, as follows:

var mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);

Then the output will instead look somewhat more human-readable, but without affecting its functionality.

{
  "firstName" : "Grant",
  "lastName" : "Hughes",
  "age" : 19
}

Example 2: Using Java 17 language features


This example introduces some Java 17 language features to help with the data modelling by making Person an abstract base class that prescribes its possible subclasses—in other words, a sealed class. I’ll also change from using an explicit age, and instead I’ll use a LocalDate to represent the person’s date of birth so the student’s age can be programmatically calculated by the application when needed.

public abstract sealed class Person permits Staff, Student {
    private final String firstName;
    private final String lastName;
    private final LocalDate dob;

    public Person(String firstName, String lastName, LocalDate dob) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.dob = dob;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public LocalDate getDob() {
        return dob;
    }

    // ...
}

The Person class has two direct subclasses, Staff and Student.

public final class Student extends Person {
    private final LocalDate graduation;

    private Student(String firstName, String lastName, LocalDate dob, LocalDate graduation) {
        super(firstName, lastName, dob);
        this.graduation = graduation;
    }

    // Simple factory method
    public static Student of(String firstName, String lastName, LocalDate dob, LocalDate graduation) {
        return new Student(firstName, lastName, dob, graduation);
    }

    public LocalDate getGraduation() {
        return graduation;
    }

    // equals, hashcode, and toString elided
}

You can serialize with driver code, which will be slightly more complex.

var dob = LocalDate.of(2002, Month.MARCH, 17);
var graduation = LocalDate.of(2023, Month.JUNE, 5);
var grant = Student.of("Grant", "Hughes", dob, graduation);

var mapper = new ObjectMapper()
                .enable(SerializationFeature.INDENT_OUTPUT)
                .registerModule(new JavaTimeModule());

try {
    var json = mapper.writeValueAsString(grant);
    System.out.println(json);
} catch (JsonProcessingException e) {
    e.printStackTrace();
}

The code above produces the following output:

{
  "firstName" : "Grant",
  "lastName" : "Hughes",
  "dob" : [ 2002, 3, 17 ],
  "graduation" : [ 2023, 6, 5 ]
}

As mentioned earlier, Jackson still requires only Java 7 as a minimum version, and it’s geared around that version. This means that if your objects depend on Java 8 APIs directly (such as classes from java.time), the serialization must use a specific Java 8 module (JavaTimeModule). This class must be registered when the mapper is created—it is not available by default.

To handle that requirement, you will also need to add a couple of extra dependencies to the Jackson libraries’ default. Here they are for a Gradle build script (written in Kotlin).

implementation("com.fasterxml.jackson.core:jackson-databind:2.13.1")
implementation("com.fasterxml.jackson.module:jackson-modules-java8:2.13.1")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.1")

Example 3: Using annotations


The first two examples made it look easy to use Jackson: You created an ObjectMapper object, and the code was automatically able to understand the structure of the Student object and render it into JSON.

However, in practice things are rarely this simple. Here are some real-world situations that can quickly arise when you use Jackson in actual production applications.

In some circumstances, you need to give Jackson a little help. For example, you might want or need to remap the field names from your class into different names in the serialized JSON. Fortunately, this is easy to do with annotations.

public class Person {
    @JsonProperty("first_name")
    private final String firstName;
    @JsonProperty("last_name")
    private final String lastName;
    private final int age;
    private final List<string> degrees;

    public Person(String firstName, String lastName, int age, List<string> degrees) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
        this.degrees = degrees;
    }

    // ... getters for all fields
}

Your code will produce some output that looks like the following:

{
  "age" : 19,
  "degrees" : [ "BA Maths", "PhD" ],
  "first_name" : "Grant",
  "last_name" : "Hughes"
}

Note that the field names are now different from the JSON keys and that a List of Java strings is being represented as a JSON array. This is the first usage of annotations in Jackson that you are seeing—but it won’t be the last.

Example 4: Deserialization with JSON


Everything so far has involved serialization of Java objects to JSON. What happens when you want to go the other way? Fortunately, the ObjectMapper provides a reading API as well as a writing API. Here is how the reading API works; this example also uses Java 17 text blocks, by the way.

var json = """
            {
                "firstName" : "Grant",
                "lastName" : "Hughes",
                "age" : 19
            }""";

var mapper = new ObjectMapper();
try {
    var grant = mapper.readValue(json, Person.class);
    System.out.println(grant);
} catch (JsonProcessingException e) {
    e.printStackTrace();
}

When you run this code, you’ll see some output like the following:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of 'javamag.jackson.ex5.Person' (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (String)"{
  "firstName" : "Grant",
  "lastName" : "Hughes",
  "age" : 19
}"; line: 2, column: 3]
  at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
  at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1904)

    // ...

  at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3597)
  at javamag.jackson.ex5.UniversityMain.main(UniversityMain.java:19)

What happened? Recall that ObjectMapper expects getters for serialization—and it wants them to conform to the JavaBeans get/setFoo() convention. ObjectMapper also expects an accessible default constructor, that is, one that takes no parameters.

However, your Person class has none of these things; in fact, all its fields are final. This means setter methods would be totally impossible even if you cheated and added a default constructor to make Jackson happy.

How are you going to resolve this? You certainly aren’t going to warp your application’s object model to comply with the requirements of JavaBeans merely to get serialization to work. Annotations come to the rescue again: You can modify the Person class as follows:

public class Person {
    private final String firstName;
    private final String lastName;
    private final int age;

    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
    public Person(@JsonProperty("first_name") String firstName,
                  @JsonProperty("last_name") String lastName,
                  @JsonProperty("age") int age) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }

    @JsonProperty("first_name")
    public String firstName() {
        return firstName;
    }

    @JsonProperty("last_name")
    public String lastName() {
        return lastName;
    }

    @JsonProperty("age")
    public int age() {
        return age;
    }

    // other methods elided
}

With these hints, this piece of JSON will be correctly deserialized.

{
    "first_name" : "Grant",
    "last_name" : "Hughes",
    "age" : 19
}

The two key annotations here are

◉ @JsonCreator, which labels a constructor or factory method that will be used to create new Java objects from JSON
◉ @JsonProperty, which maps JSON field names to parameter locations for object creation or for serialization

By adding @JsonProperty to your methods, these methods will be used to provide the values for serialization. If the annotation is added to a constructor or method parameter, it marks where the value for deserialization must be applied.

These annotations allow you to write simple code that can round-trip between JSON and Java objects, as follows:

var mapper = new ObjectMapper()
                    .enable(SerializationFeature.INDENT_OUTPUT);
try {
    var grant = mapper.readValue(json, Person.class);
    System.out.println(grant);

    var parsedJson = mapper.writeValueAsString(grant);
    System.out.println(parsedJson);
} catch (JsonProcessingException e) {
    e.printStackTrace();
}

Example 5: Custom serialization


The first four examples explored two different approaches to Jackson serialization. The simplest approaches required no changes to your code but relied upon the existence of a default constructor and JavaBeans conventions. This may not be convenient for modern applications.

The second approach offered much more flexibility, but it relied upon the use of Jackson annotations, which means your code now has an explicit, direct dependency upon the Jackson libraries.

What if neither of these is an acceptable design constraint? The answer is custom serialization.

Consider the following class, which has no default constructor, immutable fields, a static factory, and Java’s record convention for getters:

public class Person {
    private final String firstName;
    private final String lastName;
    private final int age;

    private Person(String firstName, String lastName, int age) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }

    public static Person of(String firstName, String lastName, int age) {
        return new Person(firstName, lastName, age);
    }

    public String firstName() {
        return firstName;
    }

    public String lastName() {
        return lastName;
    }

    public int age() {
        return age;
    }

}

Suppose you cannot change this code or introduce a direct coupling to Jackson. That’s a real-world constraint: You may be working with a JAR file and might not have access to the source code of this class.

Here is a solution.

public class PersonSerializer extends StdSerializer<person> {
    public PersonSerializer() {
        this(null);
    }

    public PersonSerializer(Class<person> t) {
        super(t);
    }

    @Override
    public void serialize(Person value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        gen.writeStartObject();
        gen.writeStringField("first_name", value.firstName());
        gen.writeStringField("last_name", value.lastName());
        gen.writeNumberField("age", value.age());
        gen.writeEndObject();
    }
}

Here is the driver code, with exception handling omitted to keep this example simple.

var grant = Person.of("Grant", "Hughes", 19);

var mapper = new ObjectMapper()
                    .enable(SerializationFeature.INDENT_OUTPUT);

var module = new SimpleModule();
module.addSerializer(Person.class, new PersonSerializer());
mapper.registerModule(module);

var json = mapper.writeValueAsString(grant);
System.out.println(json);

This example is very simple; in more-complex scenarios the need arises to traverse an entire object tree, rather than just handling simple string or primitive fields. Those requirements can significantly complicate the process of writing a custom serializer for your domain types.

Example 6: Java 17 records


To finish on an upbeat note: Jackson handles Java records seamlessly. The following code shows how it works; again, exception handling is omitted.

Public record Person(String firstName, String lastName, int age) {}

var grant = new Person("Grant", "Hughes", 19);

var mapper = new ObjectMapper()
                .enable(SerializationFeature.INDENT_OUTPUT);

var json = mapper.writeValueAsString(grant);
System.out.println(json);

var obj = mapper.readValue(json, Person.class);
System.out.println(obj);

This code round-trips the grant object without any problems whatsoever. Jackson’s record-handling capability, which is important for many modern applications, provides yet another great reason to upgrade your Java version and start building your domain models using records and sealed types wherever it is appropriate to do so.

Source: oracle.com

Friday, December 9, 2022

Quiz yourself: Defining the structure of a Java class

Oracle Java Certification, Java Tutorial and Materials, Java Guides, Oracle Java Certification, Oracle Java Learning

Test your knowledge of Java classes, such as their valid names, the use of variables inside a method, and the number of allowable import statements.

Which of the following statements are correct about a Java class? Choose two.

A. A Java class must have a name shown in the source code.
B. A Java class may have several local variables with the same name inside the same method.
C. A Java class may have several import statements.
D. An underscore character “_” is a valid Java class name.

Answer. Not all classes have an explicit name shown in the source code. For example, Java provides anonymous classes, such as the following:

Runnable r = new Runnable(){ public void run(){
  System.out.print("Do nothing!");
  }
};
r.run();

The run method of the Runnable interface is abstract, and yet you can see that there is a real object because you instantiated it and can invoke the run method. This shows that some concrete class, which implements the Runnable interface, exists. The variable r is a reference to an instance of that class, but the class name is not known in the source code. Therefore, option A is incorrect.

Variables are visible only within the scope in which they were defined. However, since a block bounded by curly braces defines a scope, you can create two sibling scopes inside one method. If you do this, two variables with the same name can coexist without a problem, such as in the following:

void twoVars() {
  { int i = 0; }
  { int i = 1; } // OK
}

In view of this, option B is correct.

Option C discusses multiple import statements. Having multiple import statements is not merely permitted—in most cases, it’s necessary to have many import statements to provide access to classes in different packages. It’s also typical to have multiple import statements providing access to each of several classes in the same package, rather than using wildcards.

Even repeating import statements for the same class or package is syntactically valid, though it would probably trigger a request during code review to tidy up the code.

The following is completely legal:

import java.util.*;
import java.util.*;

public class MyClass { // OK
}

By the way, if a class defines more than one package statement—whether specifying the same package name or a different package name—compilation would fail. Thus, the following would not compile:

package a.b.c;
package a.b.c; // NOT OK

public class MyClass {
}

Because option C asks only if multiple import statements are permitted, option C is correct.

As for option D, through Java 8 the single underscore character was a valid identifier and could be used as a class name, a method name, or a variable name.

In Java 8, a warning during compilation indicated that this character was reserved for future language changes. However, the warning did not prevent its use. But then, beginning with Java 9, the single underscore character was defined as a keyword and therefore is no longer valid as an identifier.

This change was described in the Java 9 summary of changes, which stated that the underscore character was not a legal name and warned that if you use the underscore character (_) as an identifier, your source code cannot be compiled.

By the way, it is still legal to use a double underscore (__) as an identifier, such as for a class name, a method name, or a variable name. You can also start a variable name with a single underscore.

public class MyClass {
    int __; // Double underscore is OK
}

Because the single underscore is a keyword and is not a legal identifier on its own any longer, option D is incorrect.

Conclusion. The correct answers are options B and C.

Source: oracle.com

Wednesday, December 7, 2022

Quiz yourself: Acceptable and unacceptable types for Java switch statements


Core Java, Oracle Java, Java Exam Prep, Java Certification, Java Tutorial and Materials, Java Quiz, Java Guides

Which switch expressions will compile successfully? Choose two.

A. var s = 1L;
switch (s) {
  case 1: {}
  default: {}
}

b. var s = 0;
switch (s>0) {
  case true: {}
  case false : {}
}

C. var s = Integer.MAX_VALUE;
switch (s+1) {
  case 0: case 1: {}; break;
  case 2: case 3, 4: {}
  case Integer.MAX_VALUE, Integer.MIN_VALUE : {}
}

D. var s = 's';
final var a = 0;
switch (s) {
  default: {break;}
  case a: {}
  case 'a': {}
}

Answer. A switch statement works with the following primitive types and their wrappers:

☉ byte
☉ short
☉ char
☉ int

In addition, you are allowed to switch on an enum or String (since Java 5). However, Boolean, long, float, and double types are prohibited. Given that a local variable declared using var takes its type from the right side of the assignment, option A attempts to switch on a long value. This is not permitted, and option A is incorrect.

As a side note, there’s a preview feature in Java 17 and Java 18 that expands the syntax, behavior, and acceptable argument types for switch significantly. Notably, you will be permitted to switch on arbitrary object types; however, at the time of writing, even though it’s permitted to switch on a wrapper such as a Double or Boolean, it remains prohibited to switch on the corresponding primitive types, and autoboxing happens. (Of course, this describes a preview feature that’s not relevant for the Java 17 exam, and it’s possible the details will change before the final release.)

In view of the previous discussion, option B—which attempts to switch on an expression of the Boolean primitive type—is also incorrect.

In option C, the switch type is presented as an Integer. This is acceptable; autounboxing will result in it being treated as an int. The arithmetic operation that adds one to the max value (2,147,483,647) will cause an overflow to the most negative value (-2,147,483,648), which is Integer.MIN_VALUE. However, that overflow does not throw an exception. Also, the question asks only if the code will compile, which it will, not if the programming logic is sound. From this, you can see option C is correct.

Option D is tricky, and two aspects demand attention.

First, one of the case expressions is a variable (specifically the int variable named a). If you ignore the preview features, Java requires that the case keyword must be followed by a constant expression, and most variables are not acceptable. However, in this example, closer inspection shows that a is, in fact, a constant expression because it’s declared as final. So, the code is acceptable from this perspective.

The second point is that you have executed the switch statement using an expression of char type, but the case expression for case a is an int. However, this is not a problem because the value of the expression a fits in the range of the char type (which is from 0 to 65,535) and the type of a is not long, float, or double—any of which would cause failure, even if they were constant expressions with a value in the acceptable range. Therefore, in this case, the expression is acceptable and option D compiles without errors. Therefore, option D is correct.

Conclusion. The correct answers are options C and D.

Source: oracle.com

Friday, December 2, 2022

Quiz yourself: What you can and can’t do with Java records

Java records are implicitly final. What does that mean in practice?


What can you declare in the body block of a Java record? Choose two.

Oracle Java, Java Exam, Java Prep, Java Preparation, Java Tutorial and Mateirals, Java Gudies, Java Materials

A. An instance variable
B. An instance method
C. An instance initialization block
D. A no-argument constructor

Answer. One of the goals of records is to approximate immutable data-carrier types. To this end, all the data elements of a record are implicitly final. Note that the record does not prevent mutation of a mutable object referred to through a final reference, however; therefore a record only approximates an immutable data carrier.

Option A is incorrect: You may not declare your instance variables in the body of a record. An example of the record syntax looks like the following:

record Car(int seats, String color) {}

Given this code, execution of new Car(5, "Red") results in an immutable object that has storage for an int value named seats and a String reference value named color. You might observe that the elements named seats and color are fields, which are commonly called instance variables.

However, for two reasons, option A is not a correct answer to this exam question. First, and most importantly, those fields are not declared in the body block of the record. Instead, they are outside the curly braces. The second, weaker, objection is that of nomenclature. Although reflection reports these as private final fields if you invoke getDeclaredFields on the Car.class object, the Java Language Specification does not refer to them this way. Rather it considers these fields to be record components.

Note that although you cannot declare instance fields in a record using the syntax used for a class, you can define class variables (that is, static variables) in a Java record.

Option B is correct: You can declare custom instance methods in a record. The record type automatically creates accessor methods for the record components, but it’s possible to define these explicitly if desired.

It’s also possible to replace the implementations of other autogenerated methods such as equals(Object o) and hashCode().

Beyond that, you can define arbitrary methods (both instance and static) according to your needs. Further, you can declare nested classes, interfaces, and other records inside a Java record.

Option C is incorrect: A record may not have an instance initialization block, but it does provide a somewhat related syntax known as a compact constructor. The compact constructor lets you interact with the initialization values prior to their being assigned to their final storage locations.

The compact constructor can also throw an exception if the construction is to be rejected.

Notably, the compact constructor cannot assign values to the final storage locations for the record components—that must be done by autogenerated code that is invoked after the compact constructor.

Option D is correct: You may declare your own constructors. A constructor with an argument type sequence matching the type sequence of the record components is called the canonical constructor. The canonical constructor is not usually coded explicitly, and if it isn’t, it will be generated automatically. The canonical constructor must assign values to the record components.

Constructors with other argument type sequences must delegate, using the this(...) delegation mechanism, in such a way that they ultimately call the canonical constructor. Since the canonical constructor must assign the record component values, noncanonical constructors cannot do this, since that would constitute multiple assignments to a final variable.

In other words, the first line of any noncanonical constructor must be an invocation of this(...), and the last element in the resulting chain must invoke the canonical constructor, as follows:

Copy code snippet
Copied to ClipboardError: Could not CopyCopied to ClipboardError: Could not Copy
record Time(int hrs, int min) {
    Time() {        // no-arg, noncanonical constructor
        this(0);    // delegates to the constructor below
    }
    Time(int hrs) { // another noncanonical constructor
                    // delegates to the autogenerated canonical constructor
        this(hrs, 0);
    }
}

Conclusion. The correct answers are options B and D.

Source: oracle.com