| Thanks for the quick reply! What I wanted to say is that the validation relies on the hashCode() and hashCode() (at least in our use case) only works if the instance is already valid. So in the use case I mentioned the validation only works if the instance is already valid, defeating the purpose of validation. JSON deserialization is working fine. When we receive a JSON string, we can create an instance of our immutable class without problems. The thing is that the deserialized instance may not follow the class invariant (since it was not created through the builder that would ensure it). Then we need validation to ensure that the deserialized instance follows indeed the class invariant (or preconditions/assumptions if you prefer). Take the following (made up) example where Son is our automatically generated immutable class and SonBuilder the automatically generated builder. We cannot (for the most part) change their code.
public final class Son {
@NotNull
private Father father;
public Father getFather() {
return father;
}
@Override
public int hashCode() {
int h = 31;
return h * father.hashCode(); }
@Override
public boolean equals(Object o) {
}
}
public final class SonBuilder {
public Son build() {
Son son = new Son();
checkInvariant(son); return son;
}
}
If we use the builder, we can be sure that the resulting Son instance is valid. However if someone sends a JSON (e.g. as part of a web service request), the deserialized instance may be invalid since deserialization does not use the builder. If the JSON does not contain father, the instance is deserialized just fine, but the field father is set to null. Nevertheless the web service has to ensure that it is dealing with correct instances. Hence at this point we need the additional bean validation. But since the field is null hashCode() will throw and we cannot properly validate the instance. Changing the hashCode() to circumvent the class invariant might be possible in some cases, but not in others. We think it's a not so good idea in our use case, since this could mask problems and errors in our automatically generated builders. In other words we like that hashCode() fails if the instance does not follow its invariant as this gives us the opportunity to discover errors. I think that the behavior of the validation is currently conceptually wrong because it requires immunity to invalid inner state from some methods (from hashCode() at least). But it's because of invalid inner state that we use validation: to be sure that we can safely use all the methods of the class. I hope I succeeded to clarify the context a bit more and thanks again! |