Skip to content

Latest commit

 

History

History
283 lines (201 loc) · 13.8 KB

File metadata and controls

283 lines (201 loc) · 13.8 KB

Overview

Table of Contents


Language facts

Item-01: Languages created before Java never adopted Checked Exceptions

Item-02: Languages created after Java rejected Checked Exceptions

Item-03: All other JVM languages rejected Checked Exceptions

  • The most popular non-java jvm language, backed by Google & JetBrains
  • The preferred language for Android development
  • The most popular functional language on jvm
  • The most popular jvm scripting language
  • Another popular functional language for jvm

Item-04: Java is the only TIOBE Index language plagued with Checked Exceptions

  • TIOBE Index measures programming language popularity
  • Exactly 1 out of the 20 most used programming languages has Checked Exceptions (java)
  • For backward compatibility, they cannot be removed from Java
  • Other JVM languages are free to avoid Checked Exceptions

Trending backwards

Item-05: Checked Exceptions are incompatible with Java 8+ Functional interfaces

Item-06: Checked Exceptions are incompatible with Java 8+ Streams

Item-07: Checked Exceptions are incompatible with Reactive programming


Dependencies

Item-08: Exception propagation creates dependencies across contexts

  1. Only a problem for Checked Exceptions (since they must be imported)
  2. The import requirement creates a compile time reference
  3. Typically, you are forced to add a <dependency> so method signatures compile
  4. Also called "Higher coupling"

Contracts/Interfaces

Item-09: Checked Exceptions leak implementation details

  1. Encapsulation failure
  2. Consider a CRUD abstraction over your data layer, throwing SQLException
    1. What if you want to swap out a NoSQL store or a cache?
    2. What if clients drive their logic based on thrown SQLException?

Item-10: Checked Exceptions become part of your API, forever

  1. Once you adopt Checked Exceptions, you're stuck with them
    1. Removing them can break client code that catches a Checked Exception
  2. As a library author, inability to make (safe) changes is undesirable

Item-11: Checked Exceptions make your library harder to use

  1. Engineers must select from the terrible options below (see Item-16)
  2. Engineers may opt for alternatives (or fork your lib to fix it)

Inconsistencies

Item-12: Sun/Oracle wrap some Checked Exceptions in RuntimeExceptions

  1. Even the authors of Java try to avoid CheckedExceptions
  2. See java.net.URI::create

Item-13: Sun was inconsistent with usage of Checked Exceptions

  1. See their policy/guidance
    1. See Violation for URISyntaxException
    2. See Violation for CloneNotSupportedException
    3. See Violation for NumberFormatException
  2. See Closeable vs AutoCloseable
    1. Even worse, Compare how InterruptedException and IOException are handled for both of these

Item-14: Checked Exceptions force you to acknowledge some Exceptions but not others

  1. Try-catch blocks are biased toward only catching Checked Exceptions
    1. And ignoring (possibly) more important RuntimeExceptions

Tools

Item-15: IDEs like Intellij, Eclipse and Netbeans all default to an anti-pattern

  • By default:
try {
    legacyMethod(); // throws Checked Exception

} catch ( SomeCheckedException ex ) { 
    ex.printStackTrace(); // <--- this is the Error hiding anti-pattern
                          // Notice the caller never has a chance to handle
}

Code bloat

Item-16: Java compiler forces you to choose among terrible options

  1. Compiler forces you to acknowledge
    • It doesn't force you to handle cleanly
    • It doesn't force you to handle RuntimeExceptions

Option-1: Ignore

  1. This is the Error Hiding anti-pattern
try {
    legacyMethod(); // throws Checked Exception

} catch ( SomeCheckedException ex ) { 
    // this is the error hiding anti-pattern
}

Option-2: Propagate

  1. Propagation spreads the problem to ALL of your callers
  2. Propagation forces you to change your method signatures in ways unrelated to your code

Option-3: Wrap

  1. Wrapper code is worthless
  2. Wrapper code hinders both comprehension & performance
  3. Wrapper code increases the indentation of your methods
    • reduces comprehension
    • increases maintenance cost
  4. If you control both caller & caller, the simpler solution is to use RuntimeException in the callee
try {
    legacyMethod(); // throws Checked Exception

} catch ( SomeCheckedException ex ) { 
     throw new RuntimeException(ex); // wraping
}

Option-4: Handle

  1. In practice, the caller is RARELY in a position to deal with the exception
    • eg. What to do when IOException thrown while closing a Connection?
  2. Exceptions are propagated up to a Component that CAN handle it.
    • This means you're using Options 1,2,3 above until you get to the real handler
    • This pollutes your method signatures
    • This forces all callers of those intermediate methods to do the same

Modern libraries

Item-17: Spring framework has zero Checked Exceptions

Item-18: SLF4j has zero Checked Exceptions

Item-19: Google Guava

  1. Has 7 unchecked exceptions
  2. Has 2 Checked Exceptions, both extend IOException (to align with existing libs)
  3. Has Throwables utilities to fix Checked Exceptions

Item-20: Apache Commons Lang

  1. lang3 has a utility to fix Checked Exceptions

More info