Tôi đang cố gắng phát triển một dự án trong Groovy và tôi đã tìm thấy một số thử nghiệm của mình không thành công theo cách kỳ lạ: Tôi có một giao diện Version extends Comparable<Version>
với hai lớp con cụ thể. Cả hai ghi đè equals(Object)
và compareTo(Version)
- tuy nhiên, nếu tôi cố so sánh hai trường hợp Version
có các loại bê tông khác nhau bằng cách sử dụng ==
, kiểm tra bình đẳng không thành công mặc dù thanh toán equals
và compareTo
vượt qua.Trong Groovy, tại sao hành vi của thay đổi '==' cho các giao diện mở rộng So sánh?
Nếu tôi xóa phần extends Comparable<Version>
của Version
, tôi nhận được hành vi mong đợi - ==
cho kết quả tương tự như equals
.
Tôi đã đọc ở đâu đó rằng các đại biểu Groovy ==
-equals()
trừ lớp thực hiện Comparable
, trong trường hợp này nó đại biểu compareTo
. Tuy nhiên, tôi đang tìm trường hợp cả hai tuyên bố hai trường hợp của Version
để được bình đẳng nhưng chưa kiểm tra ==
.
Tôi đã tạo SSCCE thể hiện hành vi này here.
Mã đầy đủ cũng được cung cấp dưới đây:
// Interface extending Comparable
interface Super extends Comparable<Super> {
int getValue()
}
class SubA implements Super {
int getValue() { 1 }
int compareTo(Super that) { this.value <=> that.value }
boolean equals(Object o) {
if (o == null) return false
if (!(o instanceof Super)) return false
this.value == o.value
}
}
class SubB implements Super {
int getValue() { 1 }
int compareTo(Super that) { this.value <=> that.value }
boolean equals(Object o) {
if (o == null) return false
if (!(o instanceof Super)) return false
this.value == o.value
}
}
// Interface not extending Comparable
interface AnotherSuper {
int getValue()
}
class AnotherSubA implements AnotherSuper {
int getValue() { 1 }
boolean equals(Object o) {
if (o == null) return false
if (!(o instanceof AnotherSuper)) return false
this.value == o.value
}
}
class AnotherSubB implements AnotherSuper {
int getValue() { 1 }
boolean equals(Object o) {
if (o == null) return false
if (!(o instanceof AnotherSuper)) return false
this.value == o.value
}
}
// Check with comparable versions
def a = new SubA()
def b = new SubB()
println "Comparable versions equality check: ${a == b}"
println "Explicit comparable equals check: ${a.equals(b)}"
println "Explicit comparable compareTo check: ${a.compareTo(b)}"
// Check with non-comparable versions
def anotherA = new AnotherSubA()
def anotherB = new AnotherSubB()
println "Non-comparable versions equality check: ${anotherA == anotherB}"
println "Explicit non-comparable equals check: ${anotherA.equals(anotherB)}"
gì tôi nhận được lại là:
Comparable versions equality check: false
Explicit comparable equals check: true
Explicit comparable compareTo check: 0
Non-comparable versions equality check: true
Explicit non-comparable equals check: true
EDIT
Tôi nghĩ rằng tôi hiểu tại sao điều này xảy ra bây giờ, nhờ các JIRA discussion rằng Poundex liên kết với bên dưới.
Từ Groovy của DefaultTypeTransformation class, được sử dụng để xử lý kiểm tra bình đẳng/so sánh, tôi cho rằng phương pháp compareEqual
là lần đầu tiên gọi khi một tuyên bố của mẫu x == y
đang được đánh giá:
public static boolean compareEqual(Object left, Object right) {
if (left == right) return true;
if (left == null || right == null) return false;
if (left instanceof Comparable) {
return compareToWithEqualityCheck(left, right, true) == 0;
}
// handle arrays on both sides as special case for efficiency
Class leftClass = left.getClass();
Class rightClass = right.getClass();
if (leftClass.isArray() && rightClass.isArray()) {
return compareArrayEqual(left, right);
}
if (leftClass.isArray() && leftClass.getComponentType().isPrimitive()) {
left = primitiveArrayToList(left);
}
if (rightClass.isArray() && rightClass.getComponentType().isPrimitive()) {
right = primitiveArrayToList(right);
}
if (left instanceof Object[] && right instanceof List) {
return DefaultGroovyMethods.equals((Object[]) left, (List) right);
}
if (left instanceof List && right instanceof Object[]) {
return DefaultGroovyMethods.equals((List) left, (Object[]) right);
}
if (left instanceof List && right instanceof List) {
return DefaultGroovyMethods.equals((List) left, (List) right);
}
if (left instanceof Map.Entry && right instanceof Map.Entry) {
Object k1 = ((Map.Entry)left).getKey();
Object k2 = ((Map.Entry)right).getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
Object v1 = ((Map.Entry)left).getValue();
Object v2 = ((Map.Entry)right).getValue();
if (v1 == v2 || (v1 != null && DefaultTypeTransformation.compareEqual(v1, v2)))
return true;
}
return false;
}
return ((Boolean) InvokerHelper.invokeMethod(left, "equals", right)).booleanValue();
}
Chú ý rằng nếu các LHS của biểu thức là một thể hiện của Comparable
, vì nó là trong ví dụ tôi cung cấp, so sánh được giao cho compareToWithEqualityCheck
:
private static int compareToWithEqualityCheck(Object left, Object right, boolean equalityCheckOnly) {
if (left == right) {
return 0;
}
if (left == null) {
return -1;
}
else if (right == null) {
return 1;
}
if (left instanceof Comparable) {
if (left instanceof Number) {
if (right instanceof Character || right instanceof Number) {
return DefaultGroovyMethods.compareTo((Number) left, castToNumber(right));
}
if (isValidCharacterString(right)) {
return DefaultGroovyMethods.compareTo((Number) left, ShortTypeHandling.castToChar(right));
}
}
else if (left instanceof Character) {
if (isValidCharacterString(right)) {
return DefaultGroovyMethods.compareTo((Character)left, ShortTypeHandling.castToChar(right));
}
if (right instanceof Number) {
return DefaultGroovyMethods.compareTo((Character)left,(Number)right);
}
}
else if (right instanceof Number) {
if (isValidCharacterString(left)) {
return DefaultGroovyMethods.compareTo(ShortTypeHandling.castToChar(left),(Number) right);
}
}
else if (left instanceof String && right instanceof Character) {
return ((String) left).compareTo(right.toString());
}
else if (left instanceof String && right instanceof GString) {
return ((String) left).compareTo(right.toString());
}
if (!equalityCheckOnly || left.getClass().isAssignableFrom(right.getClass())
|| (right.getClass() != Object.class && right.getClass().isAssignableFrom(left.getClass())) //GROOVY-4046
|| (left instanceof GString && right instanceof String)) {
Comparable comparable = (Comparable) left;
return comparable.compareTo(right);
}
}
if (equalityCheckOnly) {
return -1; // anything other than 0
}
throw new GroovyRuntimeException(
MessageFormat.format("Cannot compare {0} with value ''{1}'' and {2} with value ''{3}''",
left.getClass().getName(),
left,
right.getClass().getName(),
right));
}
xuống gần b ottom, phương pháp có một khối đại diện cho so sánh với phương pháp compareTo
, nhưng chỉ khi một số điều kiện nhất định được đáp ứng. Trong ví dụ tôi cung cấp, không có điều kiện nào trong số này thỏa mãn, bao gồm kiểm tra isAssignableFrom
, vì các lớp mẫu tôi cung cấp (và mã trong dự án của tôi mang lại cho tôi vấn đề) là anh chị em và do đó không thể gán cho nhau.
Tôi cho rằng tôi hiểu tại sao kiểm tra được không bây giờ, nhưng tôi vẫn còn bối rối hơn những điều sau đây:
- Làm thế nào để làm được việc này?
- Lý do đằng sau điều này là gì? Đây có phải là lỗi hoặc tính năng thiết kế không? Có lý do nào khiến hai lớp con của một lớp siêu phổ biến không nên so sánh với nhau?
Có vẻ như bạn có thể có https://jira.codehaus.org/browse/GROOVY-3364 này (tôi đã thử địa phương với phiên bản 2.4.0 và thấy kết quả tương tự như bạn) – Poundex
@Poundex Cảm ơn bạn đã liên kết. Tôi đã thấy một trong các nhận xét đề cập rằng '<=>' và '==' đi qua [ở đây] (https://github.com/groovy/groovy-core/blob/master/src/main/org/codehaus/groovy/ thời gian chạy/typehandling/DefaultTypeTransformation.java) - của sở thích cụ thể là 'compareToWithEqualityCheck' và' compareEqual'. Tôi vẫn không chắc chắn chính xác những gì đang xảy ra, mặc dù. – Tagc