मैं ग्रोवी में एक प्रोजेक्ट विकसित करने की कोशिश कर रहा हूं और मुझे अपने कुछ परीक्षणों को एक अजीब तरीके से विफल करने में मिला है: मेरे पास दो ठोस उप-वर्गों के साथ एक इंटरफ़ेस Version extends Comparable<Version>
है। दोनों ओवरराइड equals(Object)
और compareTo(Version)
- लेकिन, अगर मैं उस ==
का उपयोग कर विभिन्न ठोस प्रकार के होते हैं Version
के दो उदाहरणों तुलना करने की कोशिश, समानता की जांच में विफल रहता है, भले ही स्पष्ट equals
और compareTo
जांचों में कामयाब।ग्रोवी में, '==' का व्यवहार तुलनात्मक रूप से विस्तारित इंटरफेस के लिए क्यों बदलता है?
अगर मैं Version
की extends Comparable<Version>
हिस्से को हटाने, मैं अपेक्षित व्यवहार मिलता है - ==
equals
होगा के रूप में एक ही परिणाम देता है।
जब तक वर्ग Comparable
लागू करता है, जिसमें मामले compareTo
प्रतिनिधियों यह मैं equals()
को कहीं पढ़ा है कि ग्रूवी प्रतिनिधियों ==
। हालांकि, मुझे ऐसे मामले मिल रहे हैं जहां दोनों Version
के दो उदाहरण बराबर होने के लिए घोषित करते हैं और फिर भी ==
चेक विफल हो जाते हैं।
मैंने एक एसएससीसीई बनाया है जो इस व्यवहार को दर्शाता है here।
// 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)}"
क्या मैं वापस है हो रही है::
पूर्ण कोड भी नीचे दी गई है
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
संपादित
मुझे लगता है कि मुझे समझ में क्यों यह अब होता है, करने के लिए धन्यवाद JIRA discussion कि पाउंडेक्स नीचे से जुड़ा हुआ है।
ग्रूवी के DefaultTypeTransformation class, जो समानता/तुलना चेकों को संभालने के लिए प्रयोग किया जाता है से, मुझे लगता है कि compareEqual
विधि पहले कहा जाता है जब प्रपत्र x == y
का एक बयान मूल्यांकन किया जा रहा है:
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();
}
सूचना है कि अगर एलएचएस अभिव्यक्ति की, Comparable
का एक उदाहरण है के रूप में यह उदाहरण मैं प्रदान में है, तुलना 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));
}
नीचे ख के पास ओटोम, विधि में एक ब्लॉक है जो compareTo
विधि, की तुलना को दर्शाता है, लेकिन केवल तभी कुछ शर्तों को संतुष्ट किया जाता है। उदाहरण में मैं प्रदान करता हूं, isAssignableFrom
चेक सहित, इनमें से कोई भी स्थिति संतुष्ट नहीं है, क्योंकि उदाहरण के लिए मैं प्रदान करता हूं (और मेरी परियोजना में कोड जो मुझे समस्या दे रहा है) भाई बहन हैं, और इसलिए एक-दूसरे को असाइन नहीं किया जा सकता है।
मैं मैं समझता हूँ क्यों चेकों अब असफल रहे हैं लगता है, लेकिन मैं अभी भी निम्नलिखित बातों पर हैरान हूँ:
- मैं इस के आसपास कैसे मिलता है?
- इसके पीछे तर्क क्या है? क्या यह एक बग या एक डिजाइन सुविधा है? क्या कोई कारण है कि एक सामान्य सुपर क्लास के दो उप-वर्गों को एक दूसरे के साथ तुलनीय नहीं होना चाहिए?
ऐसा लगता है कि आपके पास यह https://jira.codehaus.org/browse/GROOVY-3364 हो सकता है (मैंने स्थानीय रूप से 2.4.0 के साथ इसे आजमाया और आपके जैसा ही परिणाम देखा) – Poundex
@Poundex लिंक के लिए धन्यवाद। मैंने देखा कि टिप्पणियों में से एक का उल्लेख है कि '<=>' और '==' [यहां] से जाएं (https://github.com/groovy/groovy-core/blob/master/src/main/org/codehaus/groovy/ रनटाइम/टाइपहेडलिंग/डिफॉल्ट टाइप टाइप ट्रांसफॉर्मेशन.जावा) - विशेष रुचि के 'तुलना करने के लिए तुलनात्मकता जांच' और 'तुलनात्मक' की तुलना करें। मुझे अभी भी यकीन नहीं है कि वास्तव में क्या हो रहा है, हालांकि। विस्तृत प्रतिक्रिया के लिए – Tagc