0

I want to create some generic abstract class:

public abstract class SomeDomainValidationException<E extends Enum<E>> extends ValidationException {

    private final E hint;

    public SomeDomainValidationException(String message, E hint) {
        super(message);
        this.hint = hint;
    }
}

where:

@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public class ValidationException extends RuntimeException {
    public ValidationException(String message) {
        super(message);
    }
}

I want to group different types of exceptions in my code, eg I want to create different types of exceptions like:

WarehouseMatchingException 
               extends SomeDomainValidationException<WarehouseWrongDatesEnum>

but of course I can't because generic class may not extend 'java.lang.Throwable'

How to create generic class that represents different types of exceptions included in some enums like WarehouseWrongDatesEnum?

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
Matley
  • 1,953
  • 4
  • 35
  • 73
  • You basically can't (see https://stackoverflow.com/a/501284/1968 for the reason why). What you *can* do of course is use type erasure and store the specific enum inside a non-generic `SomeDomainValidationException` in an `object`. It's not very satisfactory but here we are. – Konrad Rudolph Jan 26 '23 at 13:12
  • Why not just have the upper bounds for your field instead of an E? – matt Jan 26 '23 at 13:14
  • 1
    Why is it generic in the first place? Do you just not want to rewrite the `hint` field and the constructor every time? – Sweeper Jan 26 '23 at 13:14
  • @Sweeper wouldn't the constructor have to be written in any case? Or did you mean the getter instead? – Thomas Jan 26 '23 at 13:16
  • @Sweeper can you give me some example? – Matley Jan 26 '23 at 13:18
  • The whole abstract exception seems useless. You save typing `private final WarehouseWrongDatesEnum hint;` and equivalents in each impl, but instead have to type `extends SomeDomainValidationException`. – Michael Jan 26 '23 at 13:20
  • 1
    I mean just inherit `WarehouseMatchingException` directly from `ValidationException`. `hint` would be of type `WarehouseWrongDatesEnum` rather than `E`. I don't understand why the generic abstract class is needed. – Sweeper Jan 26 '23 at 13:21
  • @Thomas Yeah you're right. I brainfarted. – Sweeper Jan 26 '23 at 13:22
  • @Sweeper especially enums. If it is an enum it should be an enum of the necessary type. Why would you have types extending an Enum? – matt Jan 27 '23 at 16:47

1 Answers1

2

You could use some interfaces to achieve something like this but this feels a little hacky. I'll post it anyway to let you decide for yourself.

First, we'll create 3 interfaces: one to return the "raw" hin, one to return the "generic" hint and one to do a cast:

//return the raw hint - this is an internal interface
interface RawxceptionHint {
    Object getRawHint();
}

//return the generic hint - this is a public interface
interface ExceptionHint<T>{
    T getHint();
}

//adapter to cast the raw hint to the generic one
interface ExceptionHintAdapter<T> extends ExceptionHint<T>, RawxceptionHint {
    default T getHint() {
        return (T)getRawHint();
    }               
}

Now you can implement your exceptions:

public abstract class SomeDomainValidationException extends ValidationException implements RawxceptionHint {

  private final Object  hint;

  public SomeDomainValidationException(String message, Object hint) {
      super(message);
      this.hint = hint;
  }

  public Object getRawHint() {
    return hint;
  }
}

class WarehouseMatchingException 
           extends SomeDomainValidationException 
           implements ExceptionHintAdapter<WarehouseWrongDatesEnum> {
  WarehouseMatchingException (String message, WarehouseWrongDatesEnum hint) {
     super(message, hint);
  }
}

Now you can access the hint via Object getRawHint() or WarehouseWrongDatesEnum getHint(). If you want to restrict access to getRawHint() you could cast the exception to ExceptionHint<WarehouseWrongDatesEnum>.

Note: as stated initially this feels somewhat hacky and I'm not sure this would even provide any benefit. Maybe there's a better solution that I can't think of right now and which supports what you need/want. However, not using that generic "middle class" SomeDomainValidationException and putting the hint and its methods directly into WarehouseMatchingException would be the safer and less hacky option.

Thomas
  • 87,414
  • 12
  • 119
  • 157