groovy
  1. groovy
  2. GROOVY-4046

1 == new Object() throws ClassCastException

    Details

    • Type: Bug Bug
    • Status: Closed Closed
    • Priority: Critical Critical
    • Resolution: Fixed
    • Affects Version/s: 1.7.0
    • Fix Version/s: 1.6.8, 1.7.1, 1.8-beta-1
    • Component/s: None
    • Labels:
      None
    • Environment:
      tested for 1.7.0 and 1.6.0
    • Testcase included:
      yes
    • Number of attachments :
      0

      Description

      Example 1:

      assertFalse(new Object() == 1) // this is ok
      assertFalse(1 == new Object()) // this throws a ClassCastException, because Groovy redirects the call to java.lang.Integer.compareTo(Integer i)

      Example 2:

      enum MyEnum

      { A, B, C }

      assertFalse(new Object() == MyEnum.A) // this is ok
      assertFalse(MyEnum.A == new Object()) // this throws a ClassCastException, because Groovy redirects the call to java.lang.Enum.compareTo(E e) where E extends java.lang.Enum<E>

        Activity

        Hide
        Roshan Dawrani added a comment -

        In the corner case of a Comparable instance being comparedTo an Object instance, the comparison made by DefaultTypeTransformation#compareToWithEqualityCheck() was becoming Object.isAssignableFrom(SomeComparableClass), which was not really serving any purpose, because all classes are assignable to Object. This meaningless condition was causing the issue in both the reported scenarios and has been fixed now.

        Show
        Roshan Dawrani added a comment - In the corner case of a Comparable instance being comparedTo an Object instance, the comparison made by DefaultTypeTransformation#compareToWithEqualityCheck() was becoming Object.isAssignableFrom(SomeComparableClass), which was not really serving any purpose, because all classes are assignable to Object. This meaningless condition was causing the issue in both the reported scenarios and has been fixed now.
        Hide
        Peter Niederwieser added a comment -

        There seems to be a broader issue here: DefaultTypeTransformation.compareToWithEqualityCheck() assumes that instances of assignment-compatible classes are comparable. I don't think this assumption is safe, even for classes that obey the Comparable contract. For example:

        class Foo implements Comparable<Foo> {
          int compareTo(Object other) {
            if (other.getClass() != Foo.class) throw new ClassCastException()
            0
          }
          
          boolean equals(Object other) {
              other.getClass() == Foo.class  
          }
        }
        
        class Bar extends Foo implements Comparable<Bar> {
          int compareTo(Object other) {
            if (other.getClass() != Bar.class) throw new ClassCastException()
            0
          }
          
          boolean equals(Object other) {
            other.getClass() == Bar.class  
          }
        }
        
        new Foo() == new Bar() // ClassCastException!
        
        Show
        Peter Niederwieser added a comment - There seems to be a broader issue here: DefaultTypeTransformation.compareToWithEqualityCheck() assumes that instances of assignment-compatible classes are comparable. I don't think this assumption is safe, even for classes that obey the Comparable contract. For example: class Foo implements Comparable<Foo> { int compareTo( Object other) { if (other.getClass() != Foo.class) throw new ClassCastException() 0 } boolean equals( Object other) { other.getClass() == Foo.class } } class Bar extends Foo implements Comparable<Bar> { int compareTo( Object other) { if (other.getClass() != Bar.class) throw new ClassCastException() 0 } boolean equals( Object other) { other.getClass() == Bar.class } } new Foo() == new Bar() // ClassCastException!
        Hide
        Edward Sumerfield added a comment -

        A potential ramification of this change is this failing test when the equals and Comparable interface exist in the same class.

        // Groovy 1.8 b4
        class EqualsCompareTest extends GroovyTestCase {

        void test_equals_along()

        { assert new EqualsOnly() == 1 }

        void test_equals_and_compare()

        { assert new EqualsAndCompare() == 1 }

        }

        class EqualsOnly {
        boolean equals(value)

        { 1 == value }
        }

        class EqualsAndCompare implements Comparable {
        boolean equals(value) { 1 == value }

        int compareTo(value)

        { 1.compareTo(value) }

        }

        Show
        Edward Sumerfield added a comment - A potential ramification of this change is this failing test when the equals and Comparable interface exist in the same class. // Groovy 1.8 b4 class EqualsCompareTest extends GroovyTestCase { void test_equals_along() { assert new EqualsOnly() == 1 } void test_equals_and_compare() { assert new EqualsAndCompare() == 1 } } class EqualsOnly { boolean equals(value) { 1 == value } } class EqualsAndCompare implements Comparable { boolean equals(value) { 1 == value } int compareTo(value) { 1.compareTo(value) } }

          People

          • Assignee:
            Roshan Dawrani
            Reporter:
            Armin Weisser
          • Votes:
            1 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: