1

I use the following enum type:

enum Status {OK,TIMEOUT,EXCEPTION}

But now I want to store what exactly the Exception is. Unfortunately you cannot instantiate an enum type. What is the best way to make something like the following possible?

switch(status)
{
 case(OK)        {System.out.println("Everything OK!");break;}
 case(TIMEOUT)   {System.out.println("Timeout :-(");break;}
 case(EXCEPTION) {System.out.println("We have an exception: "+status.exception);break;}
}

My ideas

  1. Class with singletons

    class Status
    {
     final Exception e;
     public final Status OK = new Status(null);
     public final Status TIMEOUT = new Status(null);
     public Status(Exception e) {this.e=e;}
    }
    

Then I could do:

 if(status==Status.OK) {System.out.println("Everything OK!");}
 else if(status==Status.TIMEOUT) {System.out.println("Timeout :-(");}
 else {System.out.println("We have an exception: "+status.exception);}

2. Several Classes

class Status {}
class StatusOK extends Status {}
class StatusTimeout extends Status {}
class StatusException extends Status
{
 final Exception e;    
 public StatusException(Exception e) {this.e=e;}    
}

Then I would need a bunch of "instanceOf"-statements.

P.S.: OK it seems that I didn't explain it clearly enough. In my program I answer requests and I store the status of the processing of those requests:

Map<Request,Status> request2Status;

Thus I cannot use something like Status.getMessage(exception); because at that position in my code I do not know which exception it was. That why I want to save it inside the status.

Chosen solution

private static class LearnStatus implements Serializable
    {               
        private static final long   serialVersionUID    = 1L;
        public static final LearnStatus OK = new LearnStatus(null);
        public static final LearnStatus TIMEOUT = new LearnStatus(null);
        public static final LearnStatus NO_TEMPLATE_FOUND = new LearnStatus(null);
        public static final LearnStatus QUERY_RESULT_EMPTY = new LearnStatus(null);
        public static final LearnStatus NO_QUERY_LEARNED = new LearnStatus(null);

        public final Exception exception;

        private LearnStatus(Exception exception) {this.exception = exception; }

        public static LearnStatus exceptionStatus(Exception cause)
        {
            if (cause == null) throw new NullPointerException();
            return new LearnStatus(cause);
        }

        @Override public String toString()
        {
            if(this==OK) {return "OK";}
            if(this==TIMEOUT) {return "timeout";}
            if(this==NO_TEMPLATE_FOUND) {return "no template found";}
            if(this==QUERY_RESULT_EMPTY) {return "query result empty";}
            if(this==NO_QUERY_LEARNED) {return "no query learned";}
            return "<summary>Exception: <details>"+exception.getLocalizedMessage()+"</details></summary>"; 
        }
    }

Problems with that

If I serialize an object with Status.OK in it, after deserialization if(status==Status.OK) does not work anymore.

New solution

I now included an enum type within the class. What do you think about it?

private static class LearnStatus implements Serializable
    {
        public enum Type {OK, TIMEOUT, NO_TEMPLATE_FOUND,QUERY_RESULT_EMPTY,NO_QUERY_LEARNED,EXCEPTION}

        public final Type type;

        private static final long   serialVersionUID    = 1L;
        public static final LearnStatus OK = new LearnStatus(Type.OK,null);
        public static final LearnStatus TIMEOUT = new LearnStatus(Type.TIMEOUT,null);
        public static final LearnStatus NO_TEMPLATE_FOUND = new LearnStatus(Type.NO_TEMPLATE_FOUND,null);
        public static final LearnStatus QUERY_RESULT_EMPTY = new LearnStatus(Type.QUERY_RESULT_EMPTY,null);
        public static final LearnStatus NO_QUERY_LEARNED = new LearnStatus(Type.NO_QUERY_LEARNED,null);

        public final Exception exception;

        private LearnStatus(Type type, Exception exception) {this.type=type;this.exception = exception;}

        public static LearnStatus exceptionStatus(Exception cause)
        {
            if (cause == null) throw new NullPointerException();
            return new LearnStatus(Type.EXCEPTION,cause);
        }

        @Override public String toString()
        {
            switch(type)
            {
                case OK:                return "OK";
                case TIMEOUT:           return "timeout";
                case NO_TEMPLATE_FOUND: return "no template found";
                case QUERY_RESULT_EMPTY:return "query result empty";
                case NO_QUERY_LEARNED:  return "no query learned";
                case EXCEPTION:         return "<summary>Exception: <details>"+exception.getLocalizedMessage()+"</details></summary>";
                default: throw new RuntimeException("switch type not handled");
            }           
        }
    }
3
  • I am not so sure that you should post a new review in the same question. It will be hard to follow all the answers if the question is a moving target. Commented Aug 10, 2012 at 12:30
  • The question is still the same as before, there was just a problem with the previously proposed solution. Commented Aug 10, 2012 at 12:31
  • But you have your subtitle New Solution and there you ask What do you think about it? and referring to some new code... Commented Aug 10, 2012 at 12:32

2 Answers 2

3

I would use an Exception unless everything is OK.

Like

   System.out.println("Everything OK!");
} catch(TimeoutException te) {
   System.out.println("Timeout :-(")
} catch(Exception e) {
   System.out.println("We have an exception: " + e);
}

I don't see any need to use an enum when Exceptions are designed to do this sort of thing.


Adding yet another layer on top of the layer between you and the original exception you can do this.

interface Status {
   String getMessage();
}

enum Statuses implements Status {
   OK("Everything OK"), TIMEOUT("Timeout :-(");

   private final String message;
   private Statuses(String message) { this.message = message; }

   String getMessage() { return message; }
}

class ExceptionStatus implement Status {
   private final String message;
   String getMessage() { return "Exception: " + message; }
}

// to print the message
System.out.println(status.getMessage());
Sign up to request clarification or add additional context in comments.

3 Comments

Sorry it seems like I didn't explain my problem clearly enough, I added to the problem description that I don't have the exception anymore at that point in time.
In that case I would say you have a layer of translation which is more of a problem than it is helpful. I would remove it so you simply have the original exception instead of adding a layer to reverse the behaviour added by the layer between you and the original exception.
The problem is like this: Map<Request,Status> request2Status = processRequests(requests); saveToDisk(request2Status); ...some days later.... Map<Request,Status> request2Status = loadFromDisk(); displayTheRequestStatusToTheUser(request2Status);
1

There are several approaches to this, but all of them depend that you don't use Enums or that you don't use them exclusively. Keep in mind that an enum is basically a class that only has well-defined singletons as value.

One possible refactoring of this is to use a normal class with well-defined singletons instead of enums:

class Status implements Serializable {
  // for serialization
  private enum InternalStatus {
    OK, TIMEOUT, EXCEPTION
  }
  public static final Status OK = new Status(null, InternalStatus.OK);
  public static final Status TIMEOUT = new Status(null, InternalStatus.TIMEOUT);

  private final Exception exception;
  private final InternalStatus internalStatus;

  private Status(Exception exception, InternalStatus internalStatus) {
    this.exception = exception;
    this.internalStatus = internalStatus;
  }

  public Exception getException() {
    return exception;
  }

  public static Status exceptionStatus(Exception cause) {
    if (cause == null) throw new NullPointerException();
    return new Status(cause, InternalStatus.EXCEPTION);
  }

  // deserialization logic handling OK and TIMEOUT being singletons
  private final Object readResolve() {
    switch (internalStatus) {
    case InternalStatus.OK:
      return OK;
    case InternalStatus.TIMEOUT:
      return TIMEOUT;
    default:
      return this;
    }
  }      
}

You can now check for status == Status.OK and status == Status.TIMEOUT. If your status variable is neither OK nor TIMEOUT, it must be caused by an exception, which you can retrieve via getException.

As a downside, you lose the switch functionality and must check via if.

5 Comments

You should make your OK and TIMEOUT final.
Thanks! If there is no solution that does it with switch functionality then this answers my problem! The only thing I don't like is the getter for a final field (could just be public in my eyes) but I guess that is a matter of personal taste.
Switch is evil anyway. If you need it only to print the message, you could assign the message to a field on constructing the object.
New Problem: After deserialization equality does not hold anymore for the static fields. Thus my if(status==Status.OK) does not work after serializing the object and deserializing it later.
@kirdie if you need serialization, then you need custom serialization logic. The easiest way is to use writeReplace and readResolve to write special objects for OK and TIMEOUT and replace them after reading.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.