ProgrammingPro #50: Java NullPointerException Fixes, JDK 23 Previews, .NET 7 Ends, Netflix's bpftop, and Google's Angular-Wiz Merge
Bite-sized actionable content, practical tutorials, and resources for programmers
Welcome to this week’s edition of ProgrammingPro!
In today’s Expert Insight, we bring you an excerpt from the recently published book,
Java Coding Problems - Second Edition, which highlights five common causes of NullPointerExceptions and their fixes, including using informative exceptions and guard conditions, illustrated through a ChainSaw class example..
News Highlights: JDK 23 previews class-file API; .NET 7 support ends; Netflix releases bpftop for eBPF optimization; and Google set to merge Angular and Wiz frameworks.
My top 5 picks from today’s learning resources:
But there’s more, so dive right in.
Stay Awesome!
Divya Anne Selvaraj
Editor-in-Chief
PS: If you have any food for thought, feedback, or would like us to find you a specific Programming learning resource for our next issue, take the survey. We will send you one Packt credit to buy a book of your choice as a thank you!
🗞️News and Analysis🔎
JDK 23 - The new features in Java 23: The upcoming September release JDK 23 is set to introduce previews of a class-file API for parsing and modifying Java class files, and primitive types in patterns, instanceof, and switch to enhance pattern matching. Read to learn more.
Microsoft Issues Reminder - End of Support for .NET 7 in May: Microsoft has announced the end of support for .NET 7 on May 14, 2024, urging developers to upgrade to .NET 8 to continue receiving security updates and technical support. Read to learn what the transition to .NET 8 involves.
Netflix Launches bpftop Aimed at Enhancing eBPF Performance Efficiency: This command-line utility is designed to optimize and monitor eBPF programs by providing real-time metrics such as program execution duration, event processing rate, and CPU usage. Read if you are working with eBPF programs.
The evolving role of developers in data analytics: This article highlights a shift towards embedding analytics directly into applications, necessitating a deeper collaboration with business stakeholders and an expanded skill set. Read to learn why developers are central to creating scalable and secure analytics solutions.
Bun 1.1 released with Windows support, stable WebSocket client and more: The update introduces the .bunx file type for cross-filesystem scripting compatibility and enhances Node.js package support, making previously incompatible packages like playwright and tensorflowjs now functional. Read to learn more.
Google to merge Angular and Wiz frameworks: This decision aims to improve Angular's functionality for performance-critical applications while incorporating Wiz's features in a manner that's open and inclusive of community feedback. Read to learn about the potential benefits for you.
JetBrains IntelliJ IDE to add K2 compiler mode: While the IDE will default to the standard K1 compiler for Kotlin code analysis, users can opt for the K2 mode, which is currently in alpha. Read to learn more about upcoming enhancements.
🎓Tutorials and Learning Resources💡
Python
Leverage OpenAI Tool calling - Building a reliable AI Agent from Scratch: This article emphasizes the importance of iterative testing and refining for building reliable AI systems. Read to learn how to build an AI agent from scratch.
The Best* Python Cheat Sheet: This Python cheat sheet, current as of Python 3.8 and released on April 1, 2024, offers a comprehensive guide to syntax, functions, and keywords. Read to learn the essentials of Python programming.
How I manage Python in 2024: This article outlines the author's preferred tools for managing Python development focusing on version and dependency management and more. Read to gain insight into contemporary Python development practices.
For more Python resources go to PythonPro
C# and .NET
🎥🎓Tutorial | Writing async/await from scratch in C# with Stephen Toub: In this vidoe, Stephen offers insights into concurrency, parallelism, asynchronous operations, and related concepts like ThreadPool, Task creation, and async iterators. Watch to gain a comprehensive understanding of the async/await pattern.
🎓Tutorial | Beautiful .NET Test Reports Using GitHub Actions: This article demonstrates an approach aimed to replicate Azure DevOps' "Test View" within GitHub. Read to discover a transparent and integrated approach.
.NET Aspire preview 4: Preview 4 enhances the development experience with Podman support, a refreshed dashboard, Entity Framework and more. Read to learn about advancements in development efficiency and database management.
C and C++
How C++ Resolves a Function Call: C++ allows for advanced features such as function overloading, operator overloading, function templates, and namespaces, which offer greater flexibility than C but can also lead to compiler errors if overused. Read to learn why understanding the compiler's algorithm is essential.
How can I tell C++ that I want to discard a nodiscard value?: In C++, the [[nodiscard]] attribute signals that a function's return value should not be ignored. Read for insights into the need for a balance between technical correctness and practical usage.
Finding memory leaks in Postgres C code: This article discusses troubleshooting a memory leak in Postgres's WAL Sender process, and ultimately finding success with the memleak program from the bcc tools collection. Read for insights into the complexities of managing memory in large, ongoing processes.
Java
🎓Tutorial | How To Get Started With New Pattern Matching in Java 21: With examples, this tutorial illustrates how pattern matching simplifies code, making it more readable by combining type checking, data extraction, and control flow. Read to learn how you can make programming in Java more efficient.
Understanding Polyglot Persistence - A Necessity for Modern Software Engineers and Architects: Polyglot Persistence represents a paradigm shift in software engineering and architecture, advocating for the use of multiple data storage technologies to optimize application performance. Read to learn how to move beyond traditional reliance on a single database system.
DataFrame and The One Billion Row Challenge: This article introduces DataFrames as a flexible, efficient solution for handling tabular data. Read to learn about the practical applications and benefits of using Java DataFrame libraries for data manipulation.
JavaScript and TypeScript
What’s the best JavaScript framework?: This article provides an in-depth comparison of the top ten JavaScript frameworks, discussing the dilemma developers face in choosing the most suitable framework due to the fast-paced innovation in the field. Read to be able to select the right framework for your needs.
Moving data from Rust to JS: This post explores different methods of transferring structured data from a Rust WebAssembly program to JavaScript, using the context of The One Billion Row Challenge (1BRC). Read to discover a creative approach to optimize this process.
🎓Tutorial | TypeScript "using" keyword and console.group are a match made in heaven: This article describes a technique for ensuring console.group calls in TypeScript are always properly closed using the new using keyword introduced in TypeScript 5.2. Read to learn how to create more manageable and automatically closed log groups.
Go
🎓Tutorial | Representing State as interfaces in Go: This article introduces a Go programming pattern which effectively separates different states' APIs while maintaining a single underlying struct for state. Read to learn about a structured way to handle state transitions in Go applications.
🎓Tutorial | Preventing SQL Injection with Golang: This article discusses preventing SQL injection in Go applications, highlighting the risk when parameters in SQL queries aren't properly sanitized. Read to learn how to secure Go applications against SQL injection attacks.
Rust
Rust developers at Google are twice as productive as C++ teams: This productivity boost is attributed to Rust's memory safety features, which significantly reduce security vulnerabilities. Read to learn more about Google's transition to Rust for certain projects.
Changes to `u128`/`i128` layout in 1.77 and 1.78: Rust has addressed inconsistencies with 128-bit integer alignment on x86 architectures, ensuring alignment with C standards and potentially improving performance at the cost of increased memory usage. Read to learn how these changes help you.
PHP
🎓Tutorial | Add extensions to Laravel Herd without Homebrew: This article highlights a method to add PHP extensions to Laravel Herd without using Homebrew, utilizing the MacPorts project instead. Read to learn how you can avoid the complexities associated with Homebrew and dependency management.
SQL
Architecture Pitfalls - Don’t use your ORM entities for everything — embrace the SQL!: This article critiques the common practice of using ORM entities for all database tasks, citing limitations in flexibility, performance, and complexity handling. Read to learn about the importance of integrating SQL knowledge into your development practices.
Ruby
14 tools and gems every Ruby developer would love: This article showcases 14 indispensable tools aimed at enhancing feedback loops, performance, debugging, and data management. Read to learn about tools that can optimize your Ruby development process.
Swift
Calling Haskell from Swift: This article delves into integrating Haskell with Swift to develop native macOS and iOS applications, focusing on calling Haskell functions from Swift. Read to learn how to effectively bridge Haskell and Swift for application development, leveraging serialization for data type compatibility and more.
Kotlin
🎓Tutorial | Kotlin Clean Architecture and CQRS: This article explores the implementation of a Kotlin-based microservice leveraging Spring WebFlux with coroutines, PostgreSQL, MongoDB, Kafka, and the Arrow-kt library. Read to learn about integrating Clean Architecture and CQRS principles in Kotlin.
🌟Best Practices, Advice, and Case Studies🚀
Enhancing Secure Software Development With ASOC Platforms: ASOC platforms integrate Application Security Testing (AST) tools into CI/CD pipelines, enhancing DevSecOps by streamlining security testing and compliance. Read for insights into the automation and intelligence the platforms bring to DevSecOps processes.
Empowering Developers - Navigating the AI Revolution in Software Engineering: This article discusses the transformative impact of AI on software development. Read for insights into the need for developers to continuously learn and adapt to leverage AI's potential.
Improving GitHub Deployments with Merge Queue: This approach, supporting over 30,000 pull requests and 4.5 million CI executions for GitHub.com, not only enhances developer velocity but also ensures the main branch's integrity by preventing the integration of failing commits. Read to learn more.
Applying Programming Best Practices to Develop a Human-Comprehensible Program: This article outlines several best practices, including the SOLID principles—Single Responsibility, Open-Closed, and more. Read to learn the significance of adhering to programming best practices to develop sustainable software.
Take the Survey, Get a Packt credit!
🧠 Expert Insight 📚
Here’s an excerpt from “Chapter 2, Objects, Immutability, Switch Expressions, and Pattern Matching” in the book, Java Coding Problems - Second Edition by Anghel Leonard.
Problem number 52: Using the enhanced NullPointerException
Take your time to dissect the following trivial code and try to identify the parts that are prone to cause a NullPointerException (these parts are
marked as numbered warnings, which will be explained after the snippet):
public final class ChainSaw {
private static final List<String> MODELS
= List.of("T300", "T450", "T700", "T800", "T900");
private final String model;
private final String power;
private final int speed;
public boolean started;
private ChainSaw(String model, String power, int speed) {
this.model = model;
this.power = power;
this.speed = speed;
}
public static ChainSaw initChainSaw(String model) {
for (String m : MODELS) {
if (model.endsWith(m)) {WARNING 3!
return new ChainSaw(model, null, WARNING 5!
(int) (Math.random() * 100));
}
}
return null; WARNING 1,2!
}
public int performance(ChainSaw[] css) {
int score = 0;
for (ChainSaw cs : css) { WARNING 3!
score += Integer.compare(
this.speed,cs.speed); WARNING 4!
}
return score;
}
public void start() {
if (!started) {
System.out.println("Started ...");
started = true;
}
}
public void stop() {
if (started) {
System.out.println("Stopped ...");
started = false;
}
}
public String getPower() {
return power; WARNING 5!
}
@Override
public String toString() {
return "ChainSaw{" + "model=" + model
+ ", speed=" + speed + ", started=" + started + '}';
}
}
You noticed the warnings? Of course, you did! There are five major scenarios behind most NullPointerException (NPEs) and each of them is present in the previous class. Prior to JDK 14, an NPE doesn’t contain detailed information about the cause. Look at this exception:
Exception in thread "main" java.lang.NullPointerException
at modern.challenge.Main.main(Main.java:21)
This message is just a starting point for the debugging process. We don’t know the root cause of this NPE or which variable is null. But, starting with JDK 14 (JEP 358), we have really helpful NPE messages. For example, in JDK 14+, the previous message looks as follows:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "modern.challenge.Strings.reverse()" because "str" is null
at modern.challenge.Main.main(Main.java:21)
The highlighted part of the message gives us important information about the root cause of this NPE. Now, we know that the str variable is null, so no need to debug further. We can just focus on how to fix this issue.
Next, let’s tackle each of the five major root causes of NPEs.
WARNING 1! NPE when calling an instance method via a null object
Consider the following code written by a client of ChainSaw:
ChainSaw cs = ChainSaw.initChainSaw("QW-T650");
cs.start(); // 'cs' is null
The client passes a chainsaw model that is not supported by this class, so the initChainSaw() method returns null. This is really bad because every time the client uses the cs variable, they will get back an NPE as follows:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "modern.challenge.ChainSaw.start()" because "cs" is null
at modern.challenge.Main.main(Main.java:9)
Instead of returning null, it is better to throw an explicit exception that informs the client that they cannot continue because we don’t have this chainsaw model (we can go for the classical IllegalArgumentException or, the more suggestive one in this case (but quite uncommon for null value handling), UnsupportedOperationException). This may be the proper fix in this case, but it is not universally true. There are cases when it is better to return an empty object (for example, an empty string, collection, or array) or a default object (for example, an object with minimalist settings) that doesn’t break the client code. Since JDK 8, we can use Optional as well. Of course, there are cases when returning null makes sense but that is more common in APIs and special situations.
WARNING 2! NPE when accessing (or modifying) the field of a null object
Consider the following code written by a client of ChainSaw:
ChainSaw cs = ChainSaw.initChainSaw("QW-T650");
boolean isStarted = cs.started; // 'cs' is null
Practically, the NPE, in this case, has the same root cause as the previous case. We try to access the started field of ChainSaw. Since this is a primitive boolean, it was initialized by JVM with false, but we cannot “see” that since we try to access this field through a null variable represented by cs.
WARNING 3! NPE when null is passed in the method argument
Consider the following code written by a client of ChainSaw:
ChainSaw cs = ChainSaw.initChainSaw(null);
You are not a good citizen if you want a null ChainSaw, but who am I to judge? It is possible for this to happen and will lead to the following NPE:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.endsWith(String)" because "model" is null
at modern.challenge.ChainSaw.initChainSaw(ChainSaw.java:25)
at modern.challenge.Main.main(Main.java:16)
The message is crystal clear. We attempt to call the String.endWith() method with a null argument represented by the model variable. To fix this issue, we have to add a guard condition to ensure that the passed model argument is not null (and eventually, not empty). In this case, we can throw an IllegalArgumentException to inform the client that we are here and we are guarding. Another approach may consist of replacing the given null with a dummy model that passes through our code without issues (for instance, since the model is a String, we can reassign an empty string, ““). However, personally, I don’t recommend this approach, not even for small methods. You never know how the code will evolve and such dummy reassignments can lead to brittle code.
WARNING 4! NPE when accessing the index value of a null array/collection
Consider the following code written by a client of ChainSaw:
ChainSaw myChainSaw = ChainSaw.initChainSaw("QWE-T800");
ChainSaw[] friendsChainSaw = new ChainSaw[]{
ChainSaw.initChainSaw("Q22-T450"),
ChainSaw.initChainSaw("QRT-T300"),
ChainSaw.initChainSaw("Q-T900"),
null, // ops!
ChainSaw.initChainSaw("QMM-T850"), // model is not supported
ChainSaw.initChainSaw("ASR-T900")
};
int score = myChainSaw.performance(friendsChainSaw);
Creating an array of ChainSaw was quite challenging in this example. We accidentally slipped a null value (actually, we did it intentionally) and an unsupported model. In return, we get the following NPE:
Exception in thread "main" java.lang.NullPointerException: Cannot read field "speed" because "cs" is null
at modern.challenge.ChainSaw.performance(ChainSaw.java:37)
at modern.challenge.Main.main(Main.java:31)
The message informs us that the cs variable is null. This is happening at line 37 in ChainSaw, so in the for loop of the performance() method. While looping the given array, our code iterated over the null value, which doesn’t have the speed field. Pay attention to this kind of scenario: even if the given array/collection itself is not null, it doesn’t mean that it cannot contain null items. So, adding a guarding check before handling each item can save us from an NPE in this case. Depending on the context, we can throw an IllegalArgumentException when the loop passes over the first null or simply ignore null values and don’t break the flow (in general, this is more suitable). Of course, using a collection that doesn’t accept null values is also a good approach (Apache Commons Collection and Guava have such collections).
WARNING 5! NPE when accessing a field via a getter
Consider the following code written by a client of ChainSaw:
ChainSaw cs = ChainSaw.initChainSaw("T5A-T800");
String power = cs.getPower();
System.out.println(power.concat(" Watts"));
And, the associated NPE:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.concat(String)" because "power" is null
at modern.challenge.Main.main(Main.java:37)
Practically, the getter getPower() returned null since the power field is null. Why? The answer is in the line return new ChainSaw(model, null, (int) (Math.random() * 100)); of the initChainSaw() method. Because we didn’t decide yet on the algorithm for calculating the power of a chainsaw, we passed null to the ChainSaw constructor. Further, the constructor simply sets the power field as this.power = power. If it was a public constructor, then most probably we would have added some guarded conditions, but being a private constructor, it is better to fix the issue right from the root and not pass that null. Since the power is a String, we can simply pass an empty string or a suggestive string such as UNKNOWN_POWER. We also may leave a TODO comment in code such as // TODO (JIRA ####): replace UNKNOWN_POWER with code. This will remind us to fix this in the next release. Meanwhile, the code has eliminated the NPE risk.
Packt library subscribers can continue reading the book for free here. Java Coding Problems - Second Edition by Anghel Leonard was published in March 2024. You can read the entire second chapter for free and buy the book here!
🛠️ Useful Tools ⚒️
bruno: an open-source API client introduced to challenge established tools like Postman by focusing on data privacy and local storage, and introducing a markup language for API requests.
spiceai: a Rust-based runtime simplifying SQL querying across diverse data sources, aiding in developing data-driven and AI-driven applications.
HyperDX Local: a containerized version of HyperDX, simplifying debugging by piping OpenTelemetry telemetry locally, featuring simplified authentication, lower memory usage, and full OpenTelemetry support.
That’s all for today.
We have an entire range of newsletters with focused content for tech pros. Subscribe to the ones you find the most useful here. Complete ProgrammingPro archives can be found here. Complete PythonPro archives are here.
📢 If your company is interested in reaching an audience of developers, software engineers, and tech decision makers, you may want to advertise with us.
If you have any feedback, leave a comment below.



