DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Newsletter
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Stop Writing Database Migration Scripts: Declarative Schema Sync for Java
  • Searching Kafka Without an Index: Lessons From Building Message Search for Kafdrop
  • ScyllaDB in Enterprise Java: High-Performance NoSQL With Eclipse JNoSQL
  • Member Spotlight: Shai Almong

Trending

  • Multi-Agent Orchestration on AWS With AgentCore Runtime and A2A
  • Agentic Systems and Design Patterns
  • Building a Software Factory With AI Agents and Workflows
  • AI Coding Agent Guardrails: Finding Gaps in Tool Permissions

Draft This article hasn't been published, so readers can't see it.

In moderation This article is waiting for review, so readers can't see it yet.

Deleted This article has been deleted, so readers can't see it.

  1. DZone
  2. Coding
  3. Java
  4. Generic Exceptions in Java

Generic Exceptions in Java

By 
Steve Chaloner
·
Jan. 07, 13 · Interview
Post on X (opens in a new tab) Share on LinkedIn (opens in a new tab)
31.5K Views

Join the DZone community Save articles, join the discussion, and download Refcards and Trend Reports with a free account.

Join for free Already a member? Log in

Wow, I can’t believe it’s been 6 weeks since I last blogged. I still need to write up a review on Plumbr, after seeing it in action at Devoxx, but to ease me back into the writing game, here’s a small(-ish) but useful spot of Java.

It’s always nice to borrow and steal concepts and ideas from other languages. Scala’s Option is one idea I really like, so I wrote an implementation in Java. It wraps an object which may or may not be null, and provides some methods to work with in a more kinda-sorta functional way. For example, the isDefined method adds an object-oriented way of checking if a value is null. It is then used in other places, such as the getOrElse method, which basically says “give me what you’re wrapping, or a fallback if it’s null”.

public T getOrElse(T fallback)
{
    return isDefined() ? get() : fallback;
}

In practice, this would replace tradional Java, such as

public void foo()
{
    String s = dao.getValue();
    if (s == null)
    {
        s = "bar";
    }
    System.out.println(s);
}

with the more concise and OO

public void foo()
{
    Option<String> s = dao.getValue();
    System.out.println(s.getOrElse("bar"));
}

However, what if I want to do something other than get a fallback value – say, throw an exception? More to the point, what if I want to throw a specific type of exception that is both specific in use and not hard-coded into Option? This requires a spot of cunning, and a splash of type inference.

Because this is Java, we can start with a new factory – ExceptionFactory. This is a basic implementation that only creates exceptions constructed with a message, but you can of course expand the code as required.

public interface ExceptionFactory<E extends Exception>
{
    E create(String message);
}

Notice the <E extends Exception> – this is the key to how this works. Using the factory, we can now add a new method to Option:

public <E extends Exception> T getOrThrow(ExceptionFactory<E> exceptionFactory,
                                          String message) throws E
{
    if (isDefined())
    {
        return get();
    }
    else
    {
        throw exceptionFactory.create(message);
    }
}

Again, notice the throws E – this is inferred from the exception factory.

And that, believe it or not, is 90% of what it takes. The one irritation is the need to have exception factories. If you can stomach this, you’re all set. Let’s define a couple of custom exceptions to see this in action.

public class ExceptionA extends Exception
{
    public ExceptionA(String message)
    {
        super(message);
    }

    public static ExceptionFactory<ExceptionA> factory()
    {
        return new ExceptionFactory<ExceptionA>()
        {
            @Override
            public ExceptionA create(String message)
            {
                return new ExceptionA(message);
            }
        };
    }
}

And the suspiciously similar ExceptionB

public class ExceptionB extends Exception
{
    public ExceptionB(String message)
    {
        super(message);
    }

    public static ExceptionFactory<ExceptionB> factory()
    {
        return new ExceptionFactory<ExceptionB>()
        {
            @Override
            public ExceptionB create(String message)
            {
                return new ExceptionB(message);
            }
        };
    }
}

And finally, throw it all together:

public class GenericExceptionTest
{
    @Test(expected = ExceptionA.class)
    public void exceptionA_throw() throws ExceptionA
    {
        Option.option(null).getOrThrow(ExceptionA.factory(),
                                       "Some message pertinent to the situation");
    }

    @Test
    public void exceptionA_noThrow() throws ExceptionA
    {
        String s = Option.option("foo").getOrThrow(ExceptionA.factory(),
                                                   "Some message pertinent to the situation");
        Assert.assertEquals("foo",
                            s);
    }

    @Test(expected = ExceptionB.class)
    public void exceptionB_throw() throws ExceptionB
    {
        Option.option(null).getOrThrow(ExceptionB.factory(),
                                       "Some message pertinent to the situation");
    }

    @Test
    public void exceptionB_noThrow() throws ExceptionB
    {
        String s = Option.option("foo").getOrThrow(ExceptionB.factory(),
                                                   "Some message pertinent to the situation");
        Assert.assertEquals("foo",
                            s);
    }
}

The important thing to notice, as highlighted in bold above, is the exception declared in the method signature is specific – it’s not a common ancestor (Exception or Throwable). This means you can now use Options in your DAO layer, your service layer, wherever, and throw specific exceptions where and how you need.

Download source: You can get the source code and tests from here – genex

Sidenote
One other interesting thing that came out of writing this was the observation that it’s possible to do this:

public void foo()
{
    throw null;
}

public void bar()
{
    try
    {
        foo();
    }
    catch (NullPointerException e)
    {
        ...
    }
}

It goes without saying that this is not a good idea :)

Java (programming language)

Published at DZone with permission of Steve Chaloner. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Stop Writing Database Migration Scripts: Declarative Schema Sync for Java
  • Searching Kafka Without an Index: Lessons From Building Message Search for Kafdrop
  • ScyllaDB in Enterprise Java: High-Performance NoSQL With Eclipse JNoSQL
  • Member Spotlight: Shai Almong

Partner Resources

×

Comments

Loading comments

The comments didn't load

Please try again in a moment.

Likes

Loading

The next likes didn't load. Please try again.

The likes didn't load

Please try again in a moment.

See who liked this

Log in or join DZone for free to see who liked this article.

Log in Join for free
  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook