Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is It Possible to Have a Constraint Annotation That Targets an Interface Instead of a Type?

Tags:

java

Is it possible to create a constraint annotation, that targets an interface but can be used to validate any concrete implementation that implements said interface?

For example, we have interface Identifiable:

public interface Identifiable {
    String getId();
}

I'd like to create a constraint, that targets List<Identifiable>:

@Documented
@Constraint(validatedBy = IdentifiablesValidator.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface Identifiables {
    // ...Constraint properties...
}

public class IdentifiablesValidator implements ConstraintValidator<Identifiables, List<Identifiable>> {
    // ...Validator logic...
}

And then apply the constraint to a target that implements the interface:

public class SomeExampleCase {
   @Identifiables()
   List<IdentifiableImpl> sut;
}

But at runtime this results in an error:

javax.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint 'com.example.Identifiables' validating type 'java.util.List<com.example.IdentifiableImpl>'. Check configuration for 'identifiables'

The constraint does work for validation, when applied to a list of the interface (e.g. List<Identifiable>). But this forces the target class (e.g. SomeExampleCase) to use the interface, rather than an implementation - which gets in the way of all types of things (e.g. Marshalling/Unmarshalling).

Is there a way to get the validator to work with an actual List<Identifiable>, without rewriting it to target `List?

Thanks in advance!

Update 1: The same issue occurs when the interface is turned into an abstract class.

like image 577
James Dudley Avatar asked Nov 01 '25 13:11

James Dudley


1 Answers

I came here with the same question and after some trials I got this working for me:

@Documented
@Constraint(validatedBy = IdentifiablesValidator.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface Identifiables {
    // ...Constraint properties...
}

public class IdentifiablesValidator implements ConstraintValidator<Identifiables, List<? extends Identifiable>> {
    // ...Validator logic...
}

The important part is the change of <? extends Interface>.

like image 197
J. S. Avatar answered Nov 04 '25 01:11

J. S.



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!