2

I have two exception classes that contain the same variable name (but in reality differ in other respects).

class FooException extends Exception
{
    public String member;
}

and

class BarException extends Exception
{
    public String member;
}

At a catch site, I want to use a multicatch

catch (FooException | BarException e) {
    System.out.println(e.member);
}

However this issues the compile error

System.out.println(e.member); symbol: variable member location: variable e of type Exception

The solution is obviously to break up the multicatch and duplicate the function body. But why doesn't the language "know" that member is available for all the exceptions in the multicatch? To me it seems to search for a common base class, and sets the type of e to that. Is that the correct way to look at this?

Dom Alvarez
  • 228
  • 2
  • 7

4 Answers4

3

To me it seems to search for a common base class, and sets the type of e to that

That's exactly what Java does. And the common type here is Exception, So e is of type Exception. And there's no field member in the class Exception.

You need to either make a superclass for all your exception that contains a member field. Or to make a catch block for every Exception type.

Edit : Or it will look for a common interface as shown by @OldCurmudgeon

vincrichaud
  • 2,218
  • 17
  • 34
2

Exceptions are just objects like any other. To be able to handle them in common ways they must implement common functionality.

interface HasMember {
    String getMember();
}

class FooException extends Exception implements HasMember {
    public String member;

    @Override
    public String getMember() {
        return member;
    }
}

class BarException extends Exception implements HasMember {
    public String member;

    @Override
    public String getMember() {
        return member;
    }
}

void throwFoo () throws FooException {
    throw new FooException();
}

void throwBar () throws BarException {
    throw new BarException();
}

public void test(String[] args) throws Exception {
    try {
        throwFoo();
        throwBar();
    } catch (FooException|BarException e) {
        System.out.println(e.getMember());
    }
}

With the addition of an abstract you can tidy this up a little.

interface HasMember {
    String getMember();
}

abstract class ExceptionWithMember extends Exception implements HasMember{
    String member;

    @Override
    public String getMember() {
        return member;
    }
}

class FooException extends ExceptionWithMember {
}

class BarException extends ExceptionWithMember {
}

void throwFoo () throws FooException {
    throw new FooException();
}

void throwBar () throws BarException {
    throw new BarException();
}

public void test(String[] args) throws Exception {
    try {
        throwFoo();
        throwBar();
    } catch (ExceptionWithMember e) {
        System.out.println(e.getMember());
    }
}
OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
0

The solution as already been proposed by OldCurmudgeon, so I will add the JLS explanation.

From the JLS 14.20. The try statement, you can find out that that kind of statement are call a "multi-catch clause"

A catch clause whose exception parameter is denoted as a union of types is called a multi-catch clause.

And a bit later, this is specified that the declared type of the parameter is the "Least Upper Bounds or lub" of those types.

The declared type of an exception parameter that denotes its type as a union with alternatives D1 | D2 | ... | Dn is lub(D1, D2, ..., Dn) (§15.12.2.7).

In your case lub(FooException | BarException) is Exception.

So inside the block statement of that catch, e is of type Exception, this explain why you don't find the member of both class.

Using the answer previously mention, the lub will be the common interface.

AxelH
  • 14,325
  • 2
  • 25
  • 55
0

But why doesn't the language "know" that member is available for all the exceptions in the multicatch?

What you're talking about is more of a dynamic typing approach. Java is statically typed.

It may seem convenient to allow you to access either of these members but it opens the door to potential bugs. Your variables are just coincidentally called the same thing. You've done nothing to tell the compiler that these types are related - why should it simply infer it?

Here's a contrived example but one that demonstrates why the compiler might not want to attempt to infer a relationship just because fields are called the same thing:

class Tree
{
    public final String bark = "brown and rough";
}

class Dog
{
    public final String bark = "loud and scary";
}

If you want to tell the compiler that types are related, Java gives you all the tools you need to be able to do that. You can use inheritance or a common interface.

Michael
  • 41,989
  • 11
  • 82
  • 128