diff --git a/src/core/lombok/EqualsAndHashCode.java b/src/core/lombok/EqualsAndHashCode.java
index 6805d214..d73afe13 100644
--- a/src/core/lombok/EqualsAndHashCode.java
+++ b/src/core/lombok/EqualsAndHashCode.java
@@ -71,6 +71,17 @@ public @interface EqualsAndHashCode {
* @return If {@code true}, always use direct field access instead of calling the getter method.
*/
boolean doNotUseGetters() default false;
+
+ /**
+ * Enables caching the result of {@code hashCode}.
+ * This is useful to prevent running expensive calculations of {@code hashCode} multiple times for fully immutable objects, where it would always return the same result.
+ * It is similar to what {@link java.lang.String#hashCode} does.
+ * This should only be used for fully immutable classes (classes with all-immutable fields).
+ * default: false
+ *
+ * @return If {@code true}, cache the result of {@code hashCode} to avoid recalculating it in future invocations.
+ */
+ boolean cacheHashCode() default false;
/**
* Any annotations listed here are put on the generated parameter of {@code equals} and {@code canEqual}.
diff --git a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
index d490738e..ade1e7c1 100644
--- a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
+++ b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
@@ -76,7 +76,8 @@ import lombok.javac.handlers.JavacHandlerUtil.MemberExistsResult;
public class HandleEqualsAndHashCode extends JavacAnnotationHandler {
private static final String RESULT_NAME = "result";
private static final String PRIME_NAME = "PRIME";
-
+ private static final String HASH_CODE_CACHE_NAME = "$hashCodeCache";
+
@Override public void handle(AnnotationValues annotation, JCAnnotation ast, JavacNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.EQUALS_AND_HASH_CODE_FLAG_USAGE, "@EqualsAndHashCode");
@@ -92,8 +93,10 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler> members = InclusionExclusionUtils.handleEqualsAndHashCodeMarking(typeNode, null, null);
- generateMethods(typeNode, source, members, null, false, access, List.nil());
+ generateMethods(typeNode, source, members, null, false, false, access, List.nil());
}
public void generateMethods(JavacNode typeNode, JavacNode source, java.util.List> members,
- Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List onParam) {
+ Boolean callSuper, boolean whineIfExists, boolean cacheHashCode, FieldAccess fieldAccess, List onParam) {
boolean notAClass = true;
if (typeNode.get() instanceof JCClassDecl) {
@@ -195,21 +198,46 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler> members, boolean callSuper, FieldAccess fieldAccess, JCTree source) {
+ public JCMethodDecl createHashCode(JavacNode typeNode, java.util.List> members, boolean callSuper, boolean cacheHashCode, FieldAccess fieldAccess, JCTree source) {
JavacTreeMaker maker = typeNode.getTreeMaker();
JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(typeNode, "Override"), List.nil());
List annsOnMethod = List.of(overrideAnnotation);
CheckerFrameworkVersion checkerFramework = getCheckerFrameworkVersion(typeNode);
+ // TODO: maybe not add this annotation if cacheHashCode is true because we *do* modify a field
if (checkerFramework.generateSideEffectFree()) annsOnMethod = annsOnMethod.prepend(maker.Annotation(genTypeRef(typeNode, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.nil()));
JCModifiers mods = maker.Modifiers(Flags.PUBLIC, annsOnMethod);
JCExpression returnType = maker.TypeIdent(CTC_INT);
ListBuffer statements = new ListBuffer();
+
+ Name cacheHashCodeName = typeNode.toName(HASH_CODE_CACHE_NAME);
+ if (cacheHashCode) {
+ JCExpression cacheNotZero = maker.Binary(CTC_NOT_EQUAL, maker.Ident(cacheHashCodeName), maker.Literal(CTC_INT, 0));
+ statements.append(maker.If(cacheNotZero, maker.Return(maker.Ident(cacheHashCodeName)), null));
+ }
Name primeName = typeNode.toName(PRIME_NAME);
Name resultName = typeNode.toName(RESULT_NAME);
@@ -305,6 +333,10 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler