Search icon
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletters
Free Learning
Arrow right icon
Java Coding Problems - Second Edition

You're reading from  Java Coding Problems - Second Edition

Product type Book
Published in Mar 2024
Publisher Packt
ISBN-13 9781837633944
Pages 798 pages
Edition 2nd Edition
Languages
Author (1):
Anghel Leonard Anghel Leonard
Profile icon Anghel Leonard

Table of Contents (16) Chapters

Preface 1. Text Blocks, Locales, Numbers, and Math 2. Objects, Immutability, Switch Expressions, and Pattern Matching 3. Working with Date and Time 4. Records and Record Patterns 5. Arrays, Collections, and Data Structures 6. Java I/O: Context-Specific Deserialization Filters 7. Foreign (Function) Memory API 8. Sealed and Hidden Classes 9. Functional Style Programming – Extending APIs 10. Concurrency – Virtual Threads and Structured Concurrency 11. Concurrency ‒ Virtual Threads and Structured Concurrency: Diving Deeper 12. Garbage Collectors and Dynamic CDS Archives 13. Socket API and Simple Web Server 14. Other Books You May Enjoy
15. Index

59. Handling the scope of a binding variable in type patterns for instanceof

From Problem 57, we know the headlines of scoping the binding variables in pattern matching. Moreover, we know from the previous problem that in the type pattern for instanceof, we have a single binding variable. It is time to see some practical examples, so let’s quickly crop this snippet from the previous problem:

if (o instanceof File file) {
  return "Saving a file of size: " 
    + String.format("%,d bytes", file.length());
}
// 'file' is out of scope here

In this snippet, the file binding variable is visible in the if-then block. Once the block is closed, the file binding variable is out of scope. But, thanks to flow scoping, a binding variable can be used in the if statement that has introduced it to define a so-called guarded pattern. Here it is:

// 'file' is created ONLY if 'instanceof' returns true
if (o instanceof File file
    // this is evaluated ONLY if 'file' was created
    && file.length() > 0 && file.length() < 1000) {
  return "Saving a file of size: " 
    + String.format("%,d bytes", file.length());
}
// another example
if (o instanceof Path path
     && Files.size(path) > 0 && Files.size(path) < 1000) {
  return "Saving a file of size: " 
    + String.format("%,d bytes", Files.size(path));
}

The conditional part that starts with the && short-circuit operator is evaluated by the compiler only if the instanceof operator is evaluated to true. This means that you cannot use the || operator instead of &&. For instance, is not logical to write this:

// this will not compile
if (o instanceof Path path
  || Files.size(path) > 0 && Files.size(path) < 1000) {...}

On the other hand, this is perfectly acceptable:

if (o instanceof Path path
  && (Files.size(path) > 0 || Files.size(path) < 1000)) {...}

We can also extend the scope of the binding variable as follows:

if (!(o instanceof String str)) {
  // str is not available here
  return "I cannot save the given object";
} else {
  return "Saving a string of size: " 
    + String.format("%,d bytes", str.length());
}

Since we negate the if-then statement, the str binding variable is available in the else branch. Following this logic, we can use early returns as well:

public int getStringLength(Object o) { 
  if (!(o instanceof String str)) {
    return 0;
  }
  return str.length();
}

Thanks to flow scoping, the compiler can set up strict boundaries for the scope of binding variables. For instance, in the following code, there is no risk of overlapping even if we keep using the same name for the binding variables:

private String strNumber(Object o) {
 if (o instanceof Integer nr) {
   return String.valueOf(nr.intValue());
 } else if (o instanceof Long nr) {
   return String.valueOf(nr.longValue());
 } else {
   // nr is out of scope here
   return "Probably a float number";
 }
}

Here, each nr binding variable has a scope that covers only its own branch. No overlapping, no conflicts! However, using the same name for the multiple binding variables can be a little bit confusing, so it is better to avoid it. For instance, we can use intNr and longNr instead of simple nr.

Another confusing scenario that is highly recommended to be avoided implies binding variables that hide fields. Check out this code:

private final String str
  = "   I am a string with leading and trailing spaces     ";
public String convert(Object o) {
  // local variable (binding variable) hides a field
  if (o instanceof String str) { 
    return str.strip(); // refers to binding variable, str
  } else {
    return str.strip(); // refers to field, str
  } 
}

So, using the same name for binding variables (this is true for any local variable as well) and fields is a bad practice that should be avoided.

In JDK 14/15, we cannot reassign binding variables because they are declared final by default. However, JDK 16+ solved the asymmetries that may occur between local and binding variables by removing the final modifier. So, starting with JDK 16+, we can reassign binding variables as in the following snippet:

String dummy = "";
private int getLength(Object o) { 
  if(o instanceof String str) {
      str = dummy; // reassigning binding variable
      // returns the length of 'dummy' not the passed 'str'
      return str.length(); 
  }
  return 0;
}

Even if this is possible, it is highly recommended to avoid such code smells and keep the world clean and happy by not re-assigning your binding variables.

You have been reading a chapter from
Java Coding Problems - Second Edition
Published in: Mar 2024 Publisher: Packt ISBN-13: 9781837633944
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €14.99/month. Cancel anytime}