mirror of
https://github.com/tiennm99/lombok.git
synced 2026-10-13 18:15:55 +00:00
Added using .getX() instead of using .x in equals, hashCode, and toString. Also updated changelog as well as the docs.
Also updated usage examples for @EqualsAndHashCode, @ToString, and @Data, which also contained some other minor issues (such as missing this. qualifiers). Still to do is to detect that getters don't exist _yet_ but will later due to @Getter or @Data.
This commit is contained in:
1 parent
c06660bd18
commit
91bb3455da
12 files changed
+172
-58
No files matched your search
@@ -17,6 +17,7 @@ Lombok Changelog
|
||||
* BUGFIX: delombok now no longer forgets to remove `import lombok.AccessLevel;`. In netbeans, that import will no longer be flagged erroneously as being unused. [Issue #100](http://code.google.com/p/projectlombok/issues/detail?id=100) and [Issue #103](http://code.google.com/p/projectlombok/issues/detail?id=103)
|
||||
* BUGFIX: While its discouraged, `import lombok.*;` is supposed to work in the vast majority of cases. In eclipse, however, it didn't. Now it does. [Issue #102](http://code.google.com/p/projectlombok/issues/detail?id=102)
|
||||
* BUGFIX: When `@Getter` or `@Setter` is applied to a multiple field declaration, such as `@Getter int x, y;`, the annotation now applies to all fields, not just the first. [Issue #54](http://code.google.com/p/projectlombok/issues/detail?id=54)
|
||||
* ENHANCEMENT: generated `toString`, `equals` and `hashCode` methods will now use `this.getX()` and `other.getX()` instead of `this.x` and `other.x` if a suitable getter is available. This behaviour is useful for proxied classes, such as the POJOs that hibernate makes. Usage of the getters can be suppressed with `@ToString/@EqualsAndHashCode(doNotUseGetters = true)`. [Issue #110](http://code.google.com/p/projectlombok/issues/detail?id=110)
|
||||
|
||||
### v0.9.2 "Hailbunny" (December 15th, 2009)
|
||||
* preliminary support for lombok on NetBeans! - thanks go to Jan Lahoda from NetBeans. [Issue #20](http://code.google.com/p/projectlombok/issues/detail?id=20)
|
||||
|
||||
@@ -46,6 +46,7 @@ import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
|
||||
import org.eclipse.jdt.internal.compiler.ast.FieldReference;
|
||||
import org.eclipse.jdt.internal.compiler.ast.IfStatement;
|
||||
import org.eclipse.jdt.internal.compiler.ast.MarkerAnnotation;
|
||||
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
|
||||
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
|
||||
import org.eclipse.jdt.internal.compiler.ast.NameReference;
|
||||
import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
|
||||
@@ -98,34 +99,87 @@ public class EclipseHandlerUtil {
|
||||
}
|
||||
}
|
||||
|
||||
private static AbstractMethodDeclaration findGetter(EclipseNode field) {
|
||||
TypeReference fieldType = ((FieldDeclaration)field.get()).type;
|
||||
boolean isBoolean = nameEquals(fieldType.getTypeName(), "boolean") && fieldType.dimensions() == 0;
|
||||
EclipseNode typeNode = field.up();
|
||||
for (String potentialGetterName : TransformationsUtil.toAllGetterNames(field.getName(), isBoolean)) {
|
||||
switch (methodExists(potentialGetterName, typeNode, false)) {
|
||||
case EXISTS_BY_LOMBOK:
|
||||
case EXISTS_BY_USER:
|
||||
for (EclipseNode potentialGetter : typeNode.down()) {
|
||||
if (potentialGetter.getKind() != Kind.METHOD) continue;
|
||||
AbstractMethodDeclaration method = (AbstractMethodDeclaration) potentialGetter.get();
|
||||
/** static getX() methods don't count. */
|
||||
if ((method.modifiers & ClassFileConstants.AccStatic) != 0) continue;
|
||||
/** Nor do getters with a non-empty parameter list. */
|
||||
if (method.arguments != null && method.arguments.length > 0) continue;
|
||||
return method;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static TypeReference getFieldType(EclipseNode field, boolean useFieldsDirectly) {
|
||||
return ((FieldDeclaration)field.get()).type;
|
||||
AbstractMethodDeclaration getter = useFieldsDirectly ? null : findGetter(field);
|
||||
if (!(getter instanceof MethodDeclaration)) {
|
||||
return ((FieldDeclaration)field.get()).type;
|
||||
}
|
||||
|
||||
return ((MethodDeclaration)getter).returnType;
|
||||
}
|
||||
|
||||
static Expression createFieldAccessor(EclipseNode field, boolean useFieldsDirectly, ASTNode source) {
|
||||
int pS = source.sourceStart, pE = source.sourceEnd;
|
||||
long p = (long)pS << 32 | pE;
|
||||
FieldReference thisX = new FieldReference(field.getName().toCharArray(), p);
|
||||
Eclipse.setGeneratedBy(thisX, source);
|
||||
thisX.receiver = new ThisReference(pS, pE);
|
||||
Eclipse.setGeneratedBy(thisX.receiver, source);
|
||||
return thisX;
|
||||
|
||||
AbstractMethodDeclaration getter = useFieldsDirectly ? null : findGetter(field);
|
||||
|
||||
if (getter == null) {
|
||||
FieldReference thisX = new FieldReference(field.getName().toCharArray(), p);
|
||||
Eclipse.setGeneratedBy(thisX, source);
|
||||
thisX.receiver = new ThisReference(pS, pE);
|
||||
Eclipse.setGeneratedBy(thisX.receiver, source);
|
||||
return thisX;
|
||||
}
|
||||
|
||||
MessageSend call = new MessageSend();
|
||||
Eclipse.setGeneratedBy(call, source);
|
||||
call.sourceStart = pS; call.sourceEnd = pE;
|
||||
call.receiver = new ThisReference(pS, pE);
|
||||
Eclipse.setGeneratedBy(call.receiver, source);
|
||||
call.selector = getter.selector;
|
||||
return call;
|
||||
}
|
||||
|
||||
static Expression createFieldAccessor(EclipseNode field, boolean useFieldsDirectly, ASTNode source, char[] receiver) {
|
||||
int pS = source.sourceStart, pE = source.sourceEnd;
|
||||
long p = (long)pS << 32 | pE;
|
||||
|
||||
NameReference ref;
|
||||
AbstractMethodDeclaration getter = useFieldsDirectly ? null : findGetter(field);
|
||||
|
||||
char[][] tokens = new char[2][];
|
||||
tokens[0] = receiver;
|
||||
tokens[1] = field.getName().toCharArray();
|
||||
long[] poss = {p, p};
|
||||
if (getter == null) {
|
||||
NameReference ref;
|
||||
|
||||
char[][] tokens = new char[2][];
|
||||
tokens[0] = receiver;
|
||||
tokens[1] = field.getName().toCharArray();
|
||||
long[] poss = {p, p};
|
||||
|
||||
ref = new QualifiedNameReference(tokens, poss, pS, pE);
|
||||
Eclipse.setGeneratedBy(ref, source);
|
||||
return ref;
|
||||
}
|
||||
|
||||
ref = new QualifiedNameReference(tokens, poss, pS, pE);
|
||||
Eclipse.setGeneratedBy(ref, source);
|
||||
return ref;
|
||||
MessageSend call = new MessageSend();
|
||||
Eclipse.setGeneratedBy(call, source);
|
||||
call.sourceStart = pS; call.sourceEnd = pE;
|
||||
call.receiver = new SingleNameReference(receiver, p);
|
||||
Eclipse.setGeneratedBy(call.receiver, source);
|
||||
call.selector = getter.selector;
|
||||
return call;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -353,7 +353,7 @@ public class HandleEqualsAndHashCode implements EclipseAnnotationHandler<EqualsA
|
||||
MessageSend hashCodeCall = new MessageSend();
|
||||
hashCodeCall.sourceStart = pS; hashCodeCall.sourceEnd = pE;
|
||||
Eclipse.setGeneratedBy(hashCodeCall, source);
|
||||
hashCodeCall.receiver = fieldAccessor;
|
||||
hashCodeCall.receiver = createFieldAccessor(field, useFieldsDirectly, source);
|
||||
hashCodeCall.selector = "hashCode".toCharArray();
|
||||
NullLiteral nullLiteral = new NullLiteral(pS, pE);
|
||||
Eclipse.setGeneratedBy(nullLiteral, source);
|
||||
|
||||
@@ -281,7 +281,7 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd
|
||||
maker.Apply(List.<JCExpression>nil(), hcMethod, List.of(fieldAccessor)));
|
||||
} else /* objects */ {
|
||||
/* this.fieldName == null ? 0 : this.fieldName.hashCode() */
|
||||
JCExpression hcCall = maker.Apply(List.<JCExpression>nil(), maker.Select(fieldAccessor, typeNode.toName("hashCode")),
|
||||
JCExpression hcCall = maker.Apply(List.<JCExpression>nil(), maker.Select(createFieldAccessor(maker, fieldNode, useFieldsDirectly), typeNode.toName("hashCode")),
|
||||
List.<JCExpression>nil());
|
||||
JCExpression thisEqualsNull = maker.Binary(JCTree.EQ, fieldAccessor, maker.Literal(TypeTags.BOT, null));
|
||||
intoResult = intoResult.append(
|
||||
|
||||
@@ -41,6 +41,7 @@ import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
|
||||
import com.sun.tools.javac.tree.JCTree.JCExpression;
|
||||
import com.sun.tools.javac.tree.JCTree.JCImport;
|
||||
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
|
||||
import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
|
||||
import com.sun.tools.javac.tree.JCTree.JCStatement;
|
||||
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
|
||||
import com.sun.tools.javac.util.List;
|
||||
@@ -269,15 +270,26 @@ public class JavacHandlerUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an expression that reads the field. Will either be {@code this.field} or {@code this.getField()} depending on whether or not there's a getter.
|
||||
*/
|
||||
static JCExpression createFieldAccessor(TreeMaker maker, JavacNode field, boolean useFieldsDirectly) {
|
||||
return createFieldAccessor(maker, field, useFieldsDirectly, maker.Ident(field.toName("this")));
|
||||
}
|
||||
|
||||
static JCExpression createFieldAccessor(TreeMaker maker, JavacNode field, boolean useFieldsDirectly, JCExpression receiver) {
|
||||
return maker.Select(receiver, ((JCVariableDecl)field.get()).name);
|
||||
private static JCMethodDecl findGetter(JavacNode field) {
|
||||
JCVariableDecl decl = (JCVariableDecl)field.get();
|
||||
JavacNode typeNode = field.up();
|
||||
for (String potentialGetterName : toAllGetterNames(decl)) {
|
||||
switch (methodExists(potentialGetterName, typeNode, false)) {
|
||||
case EXISTS_BY_LOMBOK:
|
||||
case EXISTS_BY_USER:
|
||||
for (JavacNode potentialGetter : typeNode.down()) {
|
||||
if (potentialGetter.getKind() != Kind.METHOD) continue;
|
||||
JCMethodDecl method = (JCMethodDecl) potentialGetter.get();
|
||||
/** static getX() methods don't count. */
|
||||
if ((method.mods.flags & Flags.STATIC) != 0) continue;
|
||||
/** Nor do getters with a non-empty parameter list. */
|
||||
if (method.params != null && method.params.size() > 0) continue;
|
||||
return method;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -286,7 +298,32 @@ public class JavacHandlerUtil {
|
||||
* @see #createFieldAccessor(TreeMaker, JavacNode)
|
||||
*/
|
||||
static JCExpression getFieldType(JavacNode field, boolean useFieldsDirectly) {
|
||||
return ((JCVariableDecl)field.get()).vartype;
|
||||
JCMethodDecl getter = useFieldsDirectly ? null : findGetter(field);
|
||||
|
||||
if (getter == null) {
|
||||
return ((JCVariableDecl)field.get()).vartype;
|
||||
}
|
||||
|
||||
return getter.restype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an expression that reads the field. Will either be {@code this.field} or {@code this.getField()} depending on whether or not there's a getter.
|
||||
*/
|
||||
static JCExpression createFieldAccessor(TreeMaker maker, JavacNode field, boolean useFieldsDirectly) {
|
||||
return createFieldAccessor(maker, field, useFieldsDirectly, maker.Ident(field.toName("this")));
|
||||
}
|
||||
|
||||
static JCExpression createFieldAccessor(TreeMaker maker, JavacNode field, boolean useFieldsDirectly, JCExpression receiver) {
|
||||
JCMethodDecl getter = useFieldsDirectly ? null : findGetter(field);
|
||||
|
||||
if (getter == null) {
|
||||
return maker.Select(receiver, ((JCVariableDecl)field.get()).name);
|
||||
}
|
||||
|
||||
JCMethodInvocation call = maker.Apply(List.<JCExpression>nil(),
|
||||
maker.Select(receiver, getter.name), List.<JCExpression>nil());
|
||||
return call;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,7 @@ public class DataExample {
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
return this.name;
|
||||
}
|
||||
|
||||
void setAge(int age) {
|
||||
@@ -19,7 +19,7 @@ public class DataExample {
|
||||
}
|
||||
|
||||
public int getAge() {
|
||||
return age;
|
||||
return this.age;
|
||||
}
|
||||
|
||||
public void setScore(double score) {
|
||||
@@ -27,11 +27,11 @@ public class DataExample {
|
||||
}
|
||||
|
||||
public double getScore() {
|
||||
return score;
|
||||
return this.score;
|
||||
}
|
||||
|
||||
public String[] getTags() {
|
||||
return tags;
|
||||
return this.tags;
|
||||
}
|
||||
|
||||
public void setTags(String[] tags) {
|
||||
@@ -39,7 +39,7 @@ public class DataExample {
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "DataExample(" + name + ", " + age + ", " + score + ", " + Arrays.deepToString(tags) + ")";
|
||||
return "DataExample(" + this.getName() + ", " + this.getAge() + ", " + this.getScore() + ", " + Arrays.deepToString(this.getTags()) + ")";
|
||||
}
|
||||
|
||||
@Override public boolean equals(Object o) {
|
||||
@@ -47,21 +47,21 @@ public class DataExample {
|
||||
if (o == null) return false;
|
||||
if (o.getClass() != this.getClass()) return false;
|
||||
DataExample other = (DataExample) o;
|
||||
if (name == null ? other.name != null : !name.equals(other.name)) return false;
|
||||
if (age != other.age) return false;
|
||||
if (Double.compare(score, other.score) != 0) return false;
|
||||
if (!Arrays.deepEquals(tags, other.tags)) return false;
|
||||
if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
|
||||
if (this.getAge() != other.getAge()) return false;
|
||||
if (Double.compare(this.getScore(), other.getScore()) != 0) return false;
|
||||
if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public int hashCode() {
|
||||
final int PRIME = 31;
|
||||
int result = 1;
|
||||
final long temp1 = Double.doubleToLongBits(score);
|
||||
result = (result*PRIME) + (name == null ? 0 : name.hashCode());
|
||||
result = (result*PRIME) + age;
|
||||
final long temp1 = Double.doubleToLongBits(this.getScore());
|
||||
result = (result*PRIME) + (this.getName() == null ? 0 : this.getName().hashCode());
|
||||
result = (result*PRIME) + this.getAge();
|
||||
result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
|
||||
result = (result*PRIME) + Arrays.deepHashCode(tags);
|
||||
result = (result*PRIME) + Arrays.deepHashCode(this.getTags());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -79,15 +79,15 @@ public class DataExample {
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public T getValue() {
|
||||
return value;
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "Exercise(name=" + name + ", value=" + value + ")";
|
||||
return "Exercise(name=" + this.getName() + ", value=" + this.getValue() + ")";
|
||||
}
|
||||
|
||||
@Override public boolean equals(Object o) {
|
||||
@@ -95,16 +95,16 @@ public class DataExample {
|
||||
if (o == null) return false;
|
||||
if (o.getClass() != this.getClass()) return false;
|
||||
Exercise<?> other = (Exercise<?>) o;
|
||||
if (name == null ? other.name != null : !name.equals(other.name)) return false;
|
||||
if (value == null ? other.value != null : !value.equals(other.value)) return false;
|
||||
if (this.getName() == null ? other.getValue() != null : !this.getName().equals(other.getName())) return false;
|
||||
if (this.getValue() == null ? other.getValue() != null : !this.getValue().equals(other.getValue())) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public int hashCode() {
|
||||
final int PRIME = 31;
|
||||
int result = 1;
|
||||
result = (result*PRIME) + (name == null ? 0 : name.hashCode());
|
||||
result = (result*PRIME) + (value == null ? 0 : value.hashCode());
|
||||
result = (result*PRIME) + (this.getName() == null ? 0 : this.getName().hashCode());
|
||||
result = (result*PRIME) + (this.getValue() == null ? 0 : this.getValue().hashCode());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,24 +8,28 @@ public class EqualsAndHashCodeExample {
|
||||
private String[] tags;
|
||||
private int id;
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override public boolean equals(Object o) {
|
||||
if (o == this) return true;
|
||||
if (o == null) return false;
|
||||
if (o.getClass() != this.getClass()) return false;
|
||||
EqualsAndHashCodeExample other = (EqualsAndHashCodeExample) o;
|
||||
if (name == null ? other.name != null : !name.equals(other.name)) return false;
|
||||
if (Double.compare(score, other.score) != 0) return false;
|
||||
if (!Arrays.deepEquals(tags, other.tags)) return false;
|
||||
if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
|
||||
if (Double.compare(this.score, other.score) != 0) return false;
|
||||
if (!Arrays.deepEquals(this.tags, other.tags)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public int hashCode() {
|
||||
final int PRIME = 31;
|
||||
int result = 1;
|
||||
final long temp1 = Double.doubleToLongBits(score);
|
||||
result = (result*PRIME) + (name == null ? 0 : name.hashCode());
|
||||
final long temp1 = Double.doubleToLongBits(this.score);
|
||||
result = (result*PRIME) + (this.name == null ? 0 : this.name.hashCode());
|
||||
result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
|
||||
result = (result*PRIME) + Arrays.deepHashCode(tags);
|
||||
result = (result*PRIME) + Arrays.deepHashCode(this.tags);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -43,8 +47,8 @@ public class EqualsAndHashCodeExample {
|
||||
if (o.getClass() != this.getClass()) return false;
|
||||
if (!super.equals(o)) return false;
|
||||
Square other = (Square) o;
|
||||
if (width != o.width) return false;
|
||||
if (height != o.height) return false;
|
||||
if (this.width != other.width) return false;
|
||||
if (this.height != other.height) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -52,8 +56,8 @@ public class EqualsAndHashCodeExample {
|
||||
final int PRIME = 31;
|
||||
int result = 1;
|
||||
result = (result*PRIME) + super.hashCode();
|
||||
result = (result*PRIME) + width;
|
||||
result = (result*PRIME) + height;
|
||||
result = (result*PRIME) + this.width;
|
||||
result = (result*PRIME) + this.height;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,10 @@ public class EqualsAndHashCodeExample {
|
||||
private String[] tags;
|
||||
private int id;
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@EqualsAndHashCode(callSuper=true)
|
||||
public static class Square extends Shape {
|
||||
private final int width, height;
|
||||
|
||||
@@ -7,6 +7,10 @@ public class ToStringExample {
|
||||
private String[] tags;
|
||||
private int id;
|
||||
|
||||
public String getName() {
|
||||
return this.getName();
|
||||
}
|
||||
|
||||
public static class Square extends Shape {
|
||||
private final int width, height;
|
||||
|
||||
@@ -16,11 +20,11 @@ public class ToStringExample {
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "Square(super=" + super.toString() + ", width=" + width + ", height=" + height + ")";
|
||||
return "Square(super=" + super.toString() + ", width=" + this.width + ", height=" + this.height + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "ToStringExample(" + name + ", " + shape + ", " + Arrays.deepToString(tags) + ")";
|
||||
return "ToStringExample(" + this.getName() + ", " + this.shape + ", " + Arrays.deepToString(this.tags) + ")";
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,10 @@ public class ToStringExample {
|
||||
private String[] tags;
|
||||
private int id;
|
||||
|
||||
public String getName() {
|
||||
return this.getName();
|
||||
}
|
||||
|
||||
@ToString(callSuper=true, includeFieldNames=true)
|
||||
public static class Square extends Shape {
|
||||
private final int width, height;
|
||||
|
||||
@@ -70,6 +70,9 @@
|
||||
Having both <code>exclude</code> and <code>of</code> generates a warning; the <code>exclude</code> parameter will be ignored in that case.
|
||||
</p><p>
|
||||
By default, any variables that start with a $ symbol are excluded automatically. You can only include them by using the 'of' parameter.
|
||||
</p><p>
|
||||
If a getter exists for a field to be included, it is called instead of using a direct field reference. This behaviour can be suppressed:<br />
|
||||
<code>@EqualsAndHashCode(doNotUseGetters = true)</code>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -59,6 +59,9 @@
|
||||
other code is forced to parse your <code>toString()</code> output anyway!
|
||||
</p><p>
|
||||
By default, any variables that start with a $ symbol are excluded automatically. You can only include them by using the 'of' parameter.
|
||||
</p><p>
|
||||
If a getter exists for a field to be included, it is called instead of using a direct field reference. This behaviour can be suppressed:<br />
|
||||
<code>@ToString(doNotUseGetters = true)</code>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user