Friday, August 25, 2023

Quiz yourself: The overloaded submit(…) methods in Java’s ExecutorService

Quiz Yourself, Java’s ExecutorService, Oracle Java Career, Java Skills, Java Jobs, Java Prep, Java Preparation, Java Tutorial and Materials

Know when to use Runnable and Callable in multithreaded code.


Given the following code fragment

00:  ExecutorService es = ...
01:  // es.submit(() -> {;} );
02:  // es.submit(() -> null );
03:  // es.submit(() -> { throw new NullPointerException(); });
04:  // es.submit(() -> { throw new IOException(); });
05:  // es.submit(() ->  new SQLException());

Which line or lines, when uncommented individually, will compile successfully? Choose one.

A. Only line 01
B. Only lines 01 and 02
C. Only lines 01, 02, and 03
D. Only lines 01, 02, 03, and 04
E. All lines will compile successfully

Answer. The java.util.concurrent.ExecutorService has three overloaded submit(...) methods.

  • <T> Future<T> submit(Callable<T> task);
  • Future<?> submit(Runnable task);
  • <T> Future<T> submit(Runnable task, T result);

Each of these methods takes an object that defines a task and returns an object that allows you to interact with that task and, in particular, obtain a result from it after it is completed. In each case, the task is defined by a particular method on the argument object, and that task-defining method is declared in the interface Callable or Runnable, depending on the submit method invoked. Those interfaces have the following forms:

public interface Runnable {
    public abstract void run();
}

public interface Callable<V> {
    V call() throws Exception;
}

Notice that there are two significant differences between them.

  • A Callable returns a value, whereas the Runnable declares a void method.
  • A Callable may throw a checked exception, but a Runnable can throw only an unchecked exception.

Consider the ExecutorService methods listed above in light of this. In the first overloaded submit(…) method, the Future will give access—after the task is completed—to the value of type T that is returned by the Callable or, if the method threw a checked exception, to that exception. This access happens using the get() method of the Future. If the task was completed normally, the get() method typically returns the value returned by the task. If the task threw an exception, the get() method throws an ExecutionException, the cause of which is the exception thrown by the task.

Did you notice the vague wording “the get() method typically returns…” in the description above? The second and third overloaded submit(…) methods both take a Runnable, so no value can be provided by the task. In the case of the second method, the Future returns null if the task is completed normally. By contrast, the Future returned by the third method will return the value passed as the second argument (named result in the signature shown) when Runnable is completed normally.

It’s time to see how the compiler will view each of the proposed tasks—defined by lambda expressions—in the quiz question.

Line 01: () -> {;}

This lambda body does not return any value, so it can implement only Runnable. The method has an empty body and correctly forms a void method. It’s perhaps a little surprising to see the semicolon standing by itself, and certainly that is redundant, but Java allows semicolons to be scattered in source code anywhere that a statement is expected. From this you can see that line 01 is valid and will compile.

Line 02: () -> null

This form creates a Callable because it returns a value. A Runnable must not return anything at all; null is a value and is not compatible with a void return. The essential detail here is that the code compiles; therefore, line 02 is also valid.

In lines 03 and 04, the lambda has a body that consistently throws an exception. A Runnable can throw only unchecked exceptions, but a Callable may throw any exception. This means that line 03 could be either a Runnable or a Callable, but line 04 must be a Callable. Again, however, the essence is that both lines are valid and will compile.

Line 05: () -> new SQLException()

This one is a little surprising in that it returns an exception, rather than throwing an exception. However, exceptions are objects, so the code forms a Callable because it returns a value. Therefore line 05 is valid and will compile.

Since all five lambdas are valid, the correct answer is option E.

Conclusion. The correct answer is option E.

Source: oracle.com

Wednesday, August 16, 2023

Quiz yourself: Using anonymous classes in Java

Quiz Yourself, Java Exam, Java Exam Prep, Java Tutorial and Materials, Java Prep, Java Preparation, Java Certification, Java Guides

How are anonymous classes related to the Liskov substitution principle?


Imagine that you are doing an audit of a third-party desktop Java application that interacts with users’ input and has the following code:

var c = new Control();
c.registerHandler(
  new Handler<Event>() {
    @Override
    void handle(Event e) {
      System.out.println("Event occurred: " + e);
    }
  }
);

Based on the provided code fragment, which statement is true about the Handler type? Choose one.

A. It must be an interface.
B. It must be an abstract class.
C. It can be a concrete class.
D. It can be either a class or an interface.
E. It can be a class, an interface, or an enum.
F. It can be a class, an interface, an enum, or a record.

Answer. In this era in which Java has lambda expressions, it’s not unusual to hear the idea that anonymous classes are irrelevant. However, anonymous inner classes provide capabilities that are not possible with lambda expressions. How is that?

In general, an anonymous class can extend a class, either a concrete or an abstract class, or it can implement an interface. If an abstract type is specialized, all the abstract methods in that type must be implemented. However, an anonymous class can declare only a single parent type, regardless of whether it’s an interface or a class. This limitation derives largely from the syntax, which provides only a single place in the code at which to define the parent type.

The declaration/instantiation of an anonymous class includes a parameter list. If the parent type is a class (either abstract or concrete), that parameter list is passed to the parent class’s constructor and, of course, there must be a matching constructor for that delegation. If the parent type is an interface, the parameter list must be empty.

An anonymous class can override methods of the parent type, implement abstract methods, and define arbitrary new methods and fields, even static ones, though that’s not likely to be useful.

From the description above, you can conclude that an anonymous class is not constrained to either implement an interface or extend an abstract class. Therefore, options A and B are incorrect.

You also know that, in general, an anonymous class can be derived from a concrete or abstract class or from an interface. This seems to make options C and D both look good, though the question requires a single answer. So, you have perhaps guessed there’s a bit more to this question, which we’ll get to in a moment.

Enum types place strict limits on their subtypes: Specifically, any such subtype must itself be an anonymous class declared inside the enum, and any such subtype is implicitly final. An enum that does not declare any anonymous subtypes is itself implicitly final. This means an anonymous class declared in the form shown in the question cannot possibly be a subtype of an enum. Therefore, you can reject both options E and F as incorrect.

Further, record types are always final, which makes option F impossible.

So, how can you choose between options C and D? Turn your attention to interfaces. Any method declared in an interface will default to being public if no explicit modifier is given, and most methods in an interface can be declared explicitly public. Static and concrete instance methods can also be declared as private. Notably, however, no interface method can have any intermediate accessibility—that is, no interface method can have package level, or protected, accessibility.

In addition to the restrictions on interface methods, Java seeks to impose the Liskov substitution principle on overriding or implementing methods. This principle broadly says that if a method substitutes for a method in a parent type, it should not cause any surprises. Putting it another way, the child’s method should be consistent with the declaration of the parent’s method.

Java seeks to enforce this guidance in several ways, and one of them is to prevent an overriding or implementing method from being less accessible than the method it replaces. This means that any method that claims to override an interface method must be public. (Note that you can’t use @Override to override a private method in any situation.)

In this case, however, the method handle(Event e) in the anonymous class has package accessibility. This can be valid only if the method being overridden also has package accessibility, and that tells you that the parent type must be a class and not an interface. That parent class could be either abstract or concrete, but the only option that’s valid is option C, which says the parent can be a concrete class.

So, you can conclude that option C is correct, and option D is incorrect.

Conclusion. The correct answer is option C.

Source: oracle.com

Monday, August 14, 2023

Inside the JVM: Arrays and how they differ from other objects

Arrays are unique objects inside the JVM, and understanding their structure makes for better coding.


The simplest way of classifying Java data items is to divide them into primitives and objects. Primitives, as most Java developers know, comprise booleans, bytes, chars, the integer variants (short, int, and long), and the floating-point variants (floats and doubles). Inside the JVM, these primitives are instantiated in a raw form. The declaration of an int creates a 32-bit signed integer field for the JVM to work with. These primitives are most often created on the operand stack that is constructed for every method invocation. (The notable exception is static primitives, which are created on the heap.)

Inside the JVM: Arrays and how they differ from other objects

In contrast to the simply allocated primitives, objects are entities that surround the data item with methods and sometimes with additional supporting fields. For example, a String object contains an array (which I’ll discuss shortly) that holds the contents of the string and supplementary fields that are used by a variety of methods defined for String. Objects are created on the heap. That is, they are allocated from free memory.

All objects—except arrays—have a constructor. If the source code does not define a constructor for a new object, a no-parameter constructor is created for it by the Java compiler. Most often, this constructor calls the default constructor in the Object class, which simply returns—that is, it does nothing.

The nature of arrays


Arrays are objects. However, inside the JVM, arrays are notably different from all other objects. The first major difference is that arrays are created by the JVM—not by an implicit or explicit call to new() by the developer. When the Java compiler first comes upon a set of brackets attached to a variable name, it emits a specific bytecode that tells the JVM to create an array. The compiler also specifies the kind of data items the array will hold (either primitives or objects) and how many dimensions the array has.

The JVM next creates an array of the appropriate size and type and wraps it up as an object. That is, all the methods available in Object—which arrays inherit—for example, toString(), are available to arrays. The elements of the last dimension of a newly created array are initialized to the default value for the data type (zero for the numeric types, null for objects).

Initializing arrays


As mentioned previously, arrays lack constructors. No default constructor is created by the Java compiler, and no constructor can be specified by the developer. One implication of this is that arrays must be initialized explicitly to their desired values. This is typically done through a for loop or directly at the time of the array declaration, as follows:

importantYears = new int[] {800, 1066, 1492,};

(Note that the comma after the last value is accepted in Java and won’t cause an error.) Java does not allow initialization of selected elements using the previous syntax. You must initialize a specific element individually.

Another curiosity of Java arrays is that they can have a size of zero.

unimportantYears = new int[0];

This code will not result in an error message. This surprising feature is used primarily by code generators, which might create an array and then discover there are no values to place in it. In this example, unimportantYears is not null; instead, it’s an empty array. In the same manner, a zero-length string is not null, but rather it’s a viable object.

Multidimensional arrays


While single-dimension arrays have their quirks, multidimensional arrays contain a lot more curious magic. Here is an example of a three-dimensional array, representing the x, y, and z dimensions of the three values for a financial transaction.

points = new int[2][3][4]; // a point or 1% in interest

When the compiler encounters this code, it emits a unique bytecode, MULTIANEWARRAY, which creates an array with dimensions that are each set to the specified size. This array is implemented as an array of arrays. That is, the first two dimensions contain only pointers to other arrays. So, for example, when you access the data item at 1, 2, 0, the 1 points not to a series of values but to an array of pointers to arrays of pointer values. Those values each point to yet another array—the array of integers. Put another way, points is an array of two pointers to arrays of three pointers to arrays of four ints. Figure 1 shows this design pictorially.

Inside the JVM: Arrays and how they differ from other objects
Figure 1. A three-dimensional array as it’s created inside the JVM

If you think of this design as a tree, you’ll note that only leaf arrays contain actual values. This is somewhat counterintuitive. Two-dimensional arrays are often thought of as tables. (Strictly speaking, there is no tabular analogy in the JVM’s representation of a two-dimensional array; it’s not a rows-and-columns construct.)

This design has important performance implications. The first is that to access an individual element in this example array, the JVM must dereference three pointers to get to the integer. For accessing individual values intermittently, that process represents little overhead. However, for multidimensional arrays where you are frequently updating all the values at once, such as via a for loop, the numerous dereferencing of pointers incurs significant overhead.

One way to reduce this overhead is to consider unfolding the arrays into a single-dimension array. For example, make it a 1 x 24 array and then map the three coordinates yourself to the intended element in the array. Then, updating all the values in the array can be done quickly with greatly reduced overhead. As with all things, performance should be measured carefully to make sure the trade-off is worthwhile.

Array size and the concept of arrays of arrays


Many Java collections have a method called size(), which returns an integer stating the number of elements in the collection. Arrays have no such method. There are several reasons for this, but the principal one is that arrays are simple Object instances—they are not collections. The Object class has no size() method, so arrays don’t either.

Arrays, however, have a property called length, which can be queried to get the number of elements in the specified array. In a single-dimension array, such as the first example in this article, the following code would be equal to 3.

importantYears.length

With multidimensional arrays, the same query gives a perhaps unexpected result. Using the previous points array, points.length is equal to 2, rather than the value of 24 that you might expect. The reason is that points is considered only as an array of two elements (which happen to be pointers to other arrays). If you want to get the size of all the dimensions, you need to write the following:

System.out.printf("\n Length of points: %d", points.length);
System.out.printf("\n Length of points[0]: %d", points[0].length);
System.out.printf("\n Length of points[0][0]: %d", points[0][0].length);

The code above prints the following:

Length of points: 2
Length of points[0]: 3
Length of points[0][0]: 4

As you can see, what you have is truly three arrays, working together to create the equivalent of a three-dimensional array. So, to get the size of each dimension, you need to specify exactly which dimension you want. (It’s somewhat counterintuitive that the zero dimension is not the first one in the array.)

Here’s an interesting question: What would happen in a multidimensional array if one of the dimensions were declared with a size of 0? For example,

strangePoints = new int[3][4][0][2]

In this declaration, all dimensions after the zero-size dimension are ignored. So, the result of this declaration is equivalent to a two-dimensional array of ints. This makes sense because a zero-size dimension would contain no pointers, so it’d be unable to point to subsequent layers.

Back inside the JVM


Eagle-eyed readers of my earlier statement about length being a field rather than a method call might wonder how a direct subclass of Object would have a field called length to begin with, as Object has no such field. The answer is that there is a little magic going on inside the Java compiler. When the compiler detects a reference to the length of an array, it emits a special bytecode, ARRAYLENGTH, which obtains the length of the array and returns it. This looks and behaves like a method call, but all method calls in the JVM require one of a small set of bytecodes, and they are implemented via the creation of a new frame with stack allocation and several other operations. None of that happens with this special bytecode.

No other Java objects have a corresponding bytecode for determining their size. This is just one of the many aspects that make arrays entirely unique entities inside the JVM. So, now when you code arrays, you’ll know there’s magic going on, and you’ll understand how to use the magic to get the behavior you’re looking for.

Source: oracle.com

Friday, August 11, 2023

Quiz yourself: Abstract classes and the difference between Java’s super() and this()

Quiz Yourself, Abstract classes, Java Career, Java Skills, Java Jobs, Java Prep, Java Preparation


Given the following two classes

01:  abstract class SupA {
02:    SupA() { this(null); }
03:    SupA(SubA s) {this.init();}
04:    abstract void init();
05:  }
06:  class SubA extends SupA {
07:    void init() {System.out.print("SubA");}
08:  }

Which statement is correct if you try to compile the code and create an instance of SubA? Choose one.

A. Compilation fails at line 02.
B. Compilation fails at line 03.
C. A runtime exception occurs at line 02.
D. A runtime exception occurs at line 03.
E. SubA is printed.
F. SubASubA is printed.

Answer. This question investigates object initialization, overridden method invocation, and the difference between this() and this.

Consider the process of instantiating and initializing an object, and assume that the class and all its parent types are fully loaded and initialized at the point of instantiation.

◉ First, the invocation of new causes the allocation of memory for the entire object, including all the parent elements of which it is made. That memory is also zeroed in this phase.
◉ Next, assuming no errors (such as running out of memory) occur in the first step, control is transferred to the constructor with an argument type sequence that matches, or is compatible with, the actual parameters of the invocation.

In contemporary releases of Java, including Java 17 and later, all constructors start with one of three code elements. First, there is an implicit call to super() with no arguments. This is followed by either an explicit call to super(...), which may take arguments, or by a call to this(...), which, again, may take arguments. Strictly, evaluation of any actual parameters to these calls executes before those delegating calls. (Note that there’s a proposal to allow explicit code to be placed before those calls, provided no reference is made to the uninitialized this object, but that’s for the future.)

The class SupA has two constructors. The one on line 02 delegates to the one on line 03 using this(null), while the one on line 03 has an implicit call to super().

The class SubA has no explicit constructors; therefore, the compiler gives it an implicit constructor, which delegates using super().

From that outline, consider the flow of construction and initialization of an instance of SubA.

First, the invocation new SubA() begins by allocating and zeroing memory for the entire object, including the storage necessary for the SubA, SupA, and Object parts. There are no instance fields in the code you see in this question, but the principle is the same.

Next, the newly allocated object is passed as the implicit this argument into the implicit constructor for SubA. That constructor immediately delegates—using super()—to the zero-argument constructor for SupA. That constructor in turn immediately delegates to the one-argument constructor for SupA on line 03 using the explicit call this(null).

The body of the constructor on line 03 begins with an implicit call to super() that was generated by the compiler. That call passes control up to the zero-argument constructor of java.lang.Object. When control returns from the constructor, any instance initialization on the SupA class would be executed, but of course there is none in this case. So, execution continues with the explicit body of the constructor. The constructor body calls this.init();, which invokes the implementation on line 07 and prints the message SubA. At that point, the constructor on line 03 is finished and control returns to the constructor on line 02, which also is finished since there’s no more code after this(null).

After all the constructors for SupA have finished, control returns to the implicit constructor of SubA. The implicit constructor would then perform any instance initialization called for by the SubA class, but there is none. At this point, the construction and initialization process has been completed.

Notice that in the description above, the message SubA was printed exactly once, which makes option E the correct answer and options A, B, C, D, and F incorrect.

To dive deeper, here are some specific considerations for clarification and additional points.


A call of the form this() (with parentheses) is a call to an overloaded constructor in the same class. Contrast this with the reference this, which is an explicit reference to the current instance of the class. The second form (this without parentheses) is the prefix used explicitly to invoke the init() method. Also note that init() is an ordinary instance method that implements the abstract method declared in SupA; Java does not attach any special meaning to the name init.

The constructor on line 02 passes null to the constructor on line 03. There’s nothing tricky here. If the constructor on line 03 attempted to refer to the object, it would cause a null pointer exception, but because no such reference is made, there is no problem.

The call to this.init() on line 03 is entirely valid and safe. It’s not possible to enter a constructor except via a call to new, and new must be followed by a concrete class name. This in turn means that the object referred to by this on line 03 must have a proper implementation of the init() method.

Creating a new instance of SubA does not cause an instance of SupA to be created, so there is only one instance, and when you call this.<something>, it will be tried on SubA. If this element is missing, it will be looked up for an inherited element in superclasses. In the case of the init() method, it will be directly present in SubA, so it will be called when the this.init(); statement runs.

One more point regarding good coding practice: Although it’s done in this question, it’s a bad idea to call overridable methods during instance initialization, for two reasons.

◉ The code that’s executed might not be what the programmer intended when the parent class was written.
◉ If the invocation occurs during initialization of a parent class but invokes an implementation in a subclass, the subclass implementation might refer to fields in the subclass that have not yet been initialized. This can cause unexpected behavior and commonly causes null pointer exceptions.

Conclusion. The correct answer is option E.

Source: oracle.com

Friday, August 4, 2023

Curly Braces #11: Writing SOLID Java code

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



In this article, I am going to talk about writing SOLID Java code. No, I’m not talking about the excellent, classic book by Steve Maguire, Writing Solid Code, now out in its 20th anniversary second edition. Rather, I’m talking about the SOLID principles of object-oriented design (OOD), as taught by Robert C. Martin.

When my own journey began, I was taught to write a lot of procedural code in assembly language and C. As a computer science student and at my first professional programming job, I wrote a mix of procedural and functional C. The object-oriented programming (OOP) movement was in full swing when I moved from C to C++, and I embraced OOP completely before moving to Java.

Then, I studied the OOP works of Grady Booch and The Unified Modeling Language User Guide by the “three amigos” (Booch, Ivar Jacobson, and James Rumbaugh) as both a software design and development paradigm and as a diagramming standard for OOP. It seemed the entire world was on board with OOP and procedural coding was in the past.

However, a few things happened: the growth of SQL databases, the emergence of the World Wide Web, and the growth of automation. With these came SQL, HTML, and bash, Python, Perl, PHP, JavaScript scripting languages, and others. These were all more functional than imperative—and certainly more functional than object oriented.

Functional programming is based on mathematical concepts, and it relies on clearly defining inputs and outputs, embracing principles such as the reduction of side effects, immutability, and referential transparency. Further, functional programming is often associated with declarative programming, where you describe what you want the computer to do, versus telling the computer exactly how to do it, by structuring your code around real-world objects and mimicking their behavior in an imperative way.

While Java is fundamentally an imperative language, Java has embraced functional programming and the declarative nature that often comes with it.

Just as you can do OOP with C, you can do functional programming in Java. For example, objects help define boundaries and perform grouping that can be useful in any type of programming; this concept works for functional programming as well. And just as objects and inheritance (or composition) allow you to divide and conquer, by building up from smaller units of implementation in OOP, you can build complex algorithms from many smaller functions.

Doing Java a SOLID


Maybe OOP concepts aren’t quite dead yet, and mature dogs such as Java are capable of new functional tricks. But SOLID is more about design than programming. For example, I would suggest that even pure functional programmers think in objects, giving entities and components names that describe what they do as much as what they are. For example, maybe you have a TradingEngine, an Orchestrator, or a UserManager component.

Regardless of the paradigm, everyone wants code to be understandable and maintainable. SOLID, which is made of the following five principles from which its name derives, fosters those very same goals:

  • SRP: The single-responsibility principle
  • OCP: The open-closed principle
  • LSP: The Liskov substitution principle
  • ISP: The interface segregation principle
  • DIP: The dependency inversion principle

Next, I’ll discuss these principles in the context of Java development.

Single-responsibility principle


SRP states that a class should have a single responsibility or purpose. Nothing else should require the class to change outside of this purpose. Take the Java Date class, for example. Notice there are no formatting methods available in that class. Additionally, older methods that came close to formatting, such as toLocaleString, have been deprecated. This is a good example of SRP.

With the Date class, there are concise methods available for creating objects that represent a date and for comparing two different Date objects (and their representative dates in terms of time). To format and display a Date object, you need to use the DateFormat class, which bears the responsibility to format a given Date according to a set of flexible criteria. For example, for this code,

Date d = new Date();
String s = DateFormat.getDateInstance().format(d);
System.out.println(s);

the output will be

July 4, 2023

If you need to change how dates are represented within the system, you change only the Date class. If you need to change the way dates are formatted for display, you change just the DateFormat class, not the Date class. Combining that functionality into one class would create a monolithic behemoth that would be susceptible to side effects and related bugs if you changed one area of responsibility within the single codebase. Following the SOLID principle of SRP helps avoid these problems.

Open-closed principle


Once a class is complete and has fulfilled its purpose, there may be a reason to extend that class but you should not modify it. Instead, you should use generalization in some form, such as inheritance or delegation, instead of modifying the source code.

Look at the DateFormat class’s Javadoc, and you’ll see that DateFormat is an abstract base class, effectively enforcing OCP. While DateFormat has methods to specify a time zone, indicate where to insert or append a formatted date into a given StringBuffer, or handle types of calendars, SimpleDateFormat extends DateFormat to add more elaborate pattern-based formatting. Moving from DateFormat to SimpleDateFormat gives you everything you had before—and a whole lot more. For example, for the following code,

Date d = new Date();
SimpleDateFormat sdf = 
    new SimpleDateFormat("YYYY-MM-dd HH:MM:SS (zzzz)");
String s = sdf.format(d);
System.out.println(s);

the output will be

2023-07-04 09:07:722 (Eastern Daylight Time)

The important point is that the DateFormat class is left untouched from a source-code perspective, eliminating the chance to adversely affect any dependent code. Instead, by extending the class, you can add new functionality by isolating changes in a new class, while the original class is still available and untouched for both existing and new code to use.

Liskov substitution principle


LSP, developed by Barbara Liskov, states that if code works with a given class, it must continue to work correctly with subclasses of that base class. Although LSP sounds simple, there are all sorts of examples that show how hard it is to enforce and test LSP.

One common example involves shapes with a Shape base class. Rectangle and Square subclasses behave differently enough that substituting subclasses and requesting the area may yield unexpected results.

Using the Java libraries as an example, the Queue family of Java collection classes looks promising for conforming to LSP. Starting with the abstract base class AbstractQueue, along with subclasses ArrayBlockingQueue and DelayQueue, I created the following simple test application, fully expecting it to conform to LSP:

public class LSPTest {
    static AbstractQueue<MyDataClass> q = 
            new ArrayBlockingQueue(100);
            //new DelayQueue();
    
    public static void main(String[] args) throws Exception {
        for ( int i = 0; i < 10; i++ ) {
            q.add( getData(i+1) );
        }

        MyDataClass first = q.element();
        System.out.println("First element data: " +first.val3);
        
        int i = 0;
        for ( MyDataClass data: q ) {
            if ( i++ == 0 ) {
                test(data, first);
            }

            System.out.println("Data element: " + data.val3);
        }
        
        MyDataClass data = q.peek();
        test(data, first);
        int elements = q.size();
        data = q.remove();
        test(data, first);
        if ( q.size() != elements-1 ) {
            throw new Exception("Failed LSP test!");
        }
        
        q.clear();
        if ( ! q.isEmpty() ) {
            throw new Exception("Failed LSP test!");
        }
    }
    
    public static MyDataClass getData(int i) {
        Random rand = new Random(i); 
        MyDataClass data = new MyDataClass();
        data.val1 = rand.nextInt(100000);
        data.val2 = rand.nextLong(100000);
        data.val3 = ""+data.val1+data.val2;
        return data;
    }
    
    public static void test(MyDataClass d1, MyDataClass d2) throws Exception{
        if ( ! d1.val3.equals(d2.val3) ) {
            throw new Exception("Failed LSP test!");
        }
    }
}

But my code doesn’t pass the LSP test! It fails for two reasons: The behavior of add in the DelayQueue class requires the elements to implement the Delayed interface, and even when that is fixed, the implementation of remove has been, well, removed. Both violate LSP.

I did find, however, that AbstractBlockingQueue and ConcurrentLinkedQueue passed the LSP tests. This is good, but I hoped there would have been more consistency.

Interface segregation principle


With OOP, it’s easy to get carried away. For example, you can create a Document interface and then define interfaces that represent other documents such as text documents, numeric documents (such as a spreadsheet), or presentation-style documents (such as slides). This is fine, but the temptation to add behavior to these already rich interfaces can add too much complexity.

For example, assume the Document interface defines basic methods to create, store, and edit documents. The next evolution of the interface might be to add formatting methods for a document and then to add methods to print a document (effectively telling a Document to “print itself”). On the surface, this makes sense, because all the behavior dealing with documents is associated with the Document interface.

However, when that’s implemented, it means all of the code to create documents, store documents, format documents, print documents, and so on—each an area of substantial complexity—comes together in a single implementation that can have drawbacks when it comes to maintenance, regression testing, and even build times.

ISP breaks these megainterfaces apart, acknowledging that creating a document is a very different implementation from formatting a document and even printing a document. Developing each of those areas of functionality requires its own center of expertise; therefore, each should be its own interface.

As a byproduct, this also means that other developers need not implement interfaces that they never intend to support. For example, you can create a slide deck and then format it to only be shared via email and never printed. Or you might decide to create a simple text-based readme file, as opposed to a richly formatted document used as marketing material.

A good example of ISP in practice in the JDK is the java.awt set of interfaces. Take Shape, for example. The interface is very focused and simple. It contains methods that check for intersection, containment, and support for path iteration of the shape’s points. However, the work of actually iterating the path for a Shape is defined in the PathIterator interface. Further, methods to draw Shape objects are defined in other interfaces and abstract base classes such as Graphics2D.

In this example, java.awt doesn’t tell a Shape to draw itself or to have a color (itself a rich area of implementation); it’s a good working example of ISP.

Dependency inversion principle


It’s natural to write higher-level code that uses lower-level utility code. Maybe you have a helper class to read files or process XML data. That’s fine, but in most cases, such as code that connects to databases, JMS servers, or other lower-level entities, it’s better not to code to them directly.

Instead, DIP says that both lower-level and higher-level constructs in code should never depend directly on one another. It’s best to place abstractions, in the form of more general interfaces, in between them. For example, in the JDK, look at the java.sql.* package or the JMS API and set of classes.

With JDBC, you can code to the Connection interface without knowing exactly which database you’re connecting to, while lower-level utility code, such as DriverManager or DataSource, hides the database-specific details from your application. Combine this with dependency injection frameworks such as Spring or Micronaut, and you can even late-bind and change the database type without changing any code.

In the JMS API, the same paradigm is used to connect to a JMS server, but it goes even further. Your application can use the Destination interface to send and receive messages without knowing whether those messages are delivered via a Topic or a Queue. Lower-level code can be written or configured to choose the message paradigm (Topic or Queue) with associated message delivery details and characteristics hidden, and your application code never needs to change. It’s abstracted by the Destination interface in between.

You should write solid SOLID code


SOLID is a rich and deep topic; there’s a lot more to learn about. You can start small. Find solace knowing that the JDK has embraced many of the OOD principles that make writing code more efficient and productive.

Source: oracle.com

Wednesday, August 2, 2023

Quiz yourself: How Java resolves access to static elements in a type declaration

Quiz Yourself, Oracle Java, Oracle Java Certification, Oracle Java Prep, Oracle Java Preparation, Oracle Java Guides, Oracle Java Learning, Oracle Java Tutorial and Materials

Oh no! The situation seems to be complicated for several reasons.


Given the following code

class SuperS {
  public static String msg = "SuperS";
  public static String method() { return "SuperM"; }
}
class SubS extends SuperS {
  private static String msg = "SubS";
  public static String method() { return "SubM"; }
}
class Super {
  static SuperS superS;
  public static void main(String[] args) {
    var v = superS;
    System.out.println(v.msg);
    System.out.println(v.method());
    v = (SubS) v;
    System.out.println(v.msg);
    System.out.println(v.method());
  }
}

What is the result? Choose one.

A. Compilation fails.

B. NullPointerException is thrown at runtime.

C. ClassCastException is thrown at runtime.

D. The following is printed:
SuperS
SuperM
SubS
SubM

E. The following is printed:
SuperS
SuperM
SuperS
SuperM

Answer. This question investigates some aspects of how Java resolves access to static elements in a type. In this question, the situation seems to be complicated for several reasons.

◉ The reference variable v is declared using var rather than an explicit type name such as SuperS.
◉ The qualifying prefix to the static elements is a variable rather than a class name.
◉ The variable v that’s used as a prefix contains a null reference rather than pointing to an actual object. You can see this because the static field superS is not explicitly initialized in the code and, as such, is guaranteed to be initialized to null.
◉ It appears that the public static field msg in the parent class is shadowed by a private field of the same name in the subclass.

Consider the first of those issues. In this question, the var pseudotype is used to request that the compiler infer the type of the variable v from the type of the expression used to initialize v. That expression is the static field superS, which has type SuperS and the value null; therefore, v is also of type SuperS and has the value null. From that point forward, there is no difference in the behavior of the variable v from how it would behave if it had been given an explicit type. In particular, var does not create dynamic typing in the way that occurs in languages such as JavaScript and Python.

From this, you know that this declaration

var v = superS;

is identical in effect to the following explicit form

SuperS v = superS;

and, in this case, it has identical effect to this form

SuperS v = null;

Note the use of a reference variable (v), instead of a class name, as the prefix in an expression referring to a static element. Java borrowed a lot of C++ syntax, and one of those syntax elements is the ability to use an instance expression as a prefix in the way used here. When static methods were added to interfaces in Java 8, this ability was not propagated to that feature, presumably because it creates ambiguous code.

Given this syntax, there’s a tendency to assume that the reference is followed to the actual object, and then the element (field or method) is found in that object (which approximately describes the behavior if the element in question were an instance feature). However, this is not a valid model for the behavior with static elements. Static elements belong to the class, not to any particular instance of that class. The compiler creates code that simply determines the target class and obtains the element from that. Notice there’s nothing like late binding or polymorphism going on in this case. As a side note, there’s also nothing of that sort going on with instance fields; late-binding behavior relates only to overridable instance methods.

This tells you that the value of the prefix object is entirely irrelevant because it plays no part in accessing static elements. That illuminates the remaining pieces of this part of the puzzle: It doesn’t matter if the reference is the null value, because it’s never used.

Also, casting the value to the subtype and reassigning it, as in the following line, is also irrelevant:

v = (SubS) v;

An assignment like that might change the value of the variable (although here it does not), but it cannot change the type of that variable, which is determined entirely by its declaration. Indeed, nothing can change the type of a variable at runtime, even though the cast on the right of the assignment does create a temporary expression that has the cast type.

One more issue to consider is whether a shadowing variable in a subclass can be less accessible than the variable that it shadows, as is the situation with the two msg fields in this example. Perhaps surprisingly, this is permitted.

Why might that be a surprise? Well, broadly, the Liskov substitution principle tells you that substitute elements in a child type should not cause surprises when they are compared with their original elements in a parent type. For this reason, an overriding method in Java cannot be less accessible than the overridden method, cannot declare checked exceptions that are not permitted from the overridden method, and (somewhat simplified) must provide an assignment-compatible return type. However, these are static fields, one shadowing the other, not overriding methods, so they’re not really substitutes, and it turns out that they’re not subject to the same rules.

What’s perhaps odder is that if a static method in a class hides a static method in a parent class, the method in the subclass is subject to these rules even though it’s not really overriding either. But, since both method() methods are declared public in this question, that does not cause a problem here.

From this, remembering that the type of the variable v is SuperS, you can determine that the code will print the text from the SuperS class twice, and it will never print the text from the SubS class. You also know that no errors are reported during compilation or execution. Therefore, the correct answer is option E and options A, B, C, and D are incorrect.

Conclusion. The correct answer is option E.

Source: oracle.com