1

I have a method like this in a reusable class:

public String buildMessageForViolation(ConstraintViolation<?> constraintViolation) {
    return constraintViolation.getPropertyPath().toString().replace(".<collection element>", "") + ": " + constraintViolation.getMessage();
}

It's called from another class like this:

 fieldValidator.appendErrorMessage(getUslValidator().buildMessageWithProperty(constraintViolations.iterator().next()) +
                (constraintViolations.size() > 1 ? ", and other errors" : ""));

So, since I expect this block to be repeated in many classes, I wanted to refactor this so the complexity is in the reusable class, not in the client code.

So, I then added this to the reusable class:

public String buildMessageForViolations(Set<ConstraintViolation<?>> constraintViolations) {
    return buildMessageForViolation(constraintViolations.iterator().next()) +
            (constraintViolations.size() > 1 ? ", and other errors" : "");
}

And then changed the client code to this:

fieldValidator.appendErrorMessage(getUslValidator().buildMessageForViolations(constraintViolations));

This does not compile, saying:

The method buildMessageForViolations(Set<ConstraintViolation<?>>) in the type USLValidator is not applicable for the arguments (Set<ConstraintViolation<ClientSpecificClassName>>)

Clearly this has something to do with the fact that I specify "?" for the nested type parameter. From my point of view, this is appropriate because the reusable class doesn't care about the type parameter of the ConstraintViolation.

Is there something simple I can do to resolve this?

Update:

I was reading an answer that was posted to this, along with subsequent edits to the answer, but then it was deleted for some reason (guess the responder gave up trying to make it right).

While the answer was still there, it at least helped me find a somewhat reasonable workaround.

The client code can instead do this:

fieldValidator.appendErrorMessage(getUslValidator().buildMessageForViolations(new HashSet<ConstraintViolation<?>>(constraintViolations)));

That's at least a little better than the original, even though there's still some boilerplate that people have to remember.

David M. Karr
  • 14,317
  • 20
  • 94
  • 199
  • Possible duplicate of [Can't cast to to unspecific nested type with generics](https://stackoverflow.com/questions/3575681/cant-cast-to-to-unspecific-nested-type-with-generics) – Sean Van Gorder Jun 22 '17 at 20:15

2 Answers2

0

Nested wildcards can cause some unexpected incompatibilities. Try this:

public String buildMessageForViolations(Set<? extends ConstraintViolation<?>> constraintViolations) {
Sean Van Gorder
  • 3,393
  • 26
  • 26
0

I would write the method as:

public String buildMessageForViolations(Set<ConstraintViolation> constraintViolations) {..}

This tells the compiler that it takes a set of ConstraintViolation of any type. I think in this case this is what you are trying to tell the compiler.

With client code like:

ConstraintViolation cv = new StringConstraintViolation();
USLValidator uslValidator = new USLValidator();

Set<ConstraintViolation> constraintViolationSet = new HashSet<>();
constraintViolationSet.add(cv);
uslValidator.buildMessageForViolations(constraintViolationSet);
Mustafa
  • 5,624
  • 3
  • 24
  • 40
  • That would almost be nice, but that doesn't actually work. That presents a compile error on the client call. – David M. Karr Jun 22 '17 at 21:05
  • It's in the original post, right after "And then changed the client code to this:". – David M. Karr Jun 22 '17 at 21:15
  • I don't see the difference between your client call and the one I have in the example above. I don't get any compile errors using the code in my answer. Perhaps our compilers or java versions are different. I tested this in intellij with Java 8. – Mustafa Jun 22 '17 at 22:14
  • Using raw types doesn't make it type-safe, it just disables the generic type checking so the problem is ignored. I wouldn't recommend it even if it worked in this case. – Sean Van Gorder Jun 23 '17 at 17:35