Paul King created GROOVY-12637:
----------------------------------

             Summary: CS: an Object method such as toString() or hashCode() 
called without a receiver inside an inner class (named or anonymous) is 
resolved to the *outer* class's override, instead of the method the inner class 
inherits
                 Key: GROOVY-12637
                 URL: https://issues.apache.org/jira/browse/GROOVY-12637
             Project: Groovy
          Issue Type: Bug
            Reporter: Paul King


Under {{@CompileStatic}}, an {{Object}} method such as {{toString()}} or 
{{hashCode()}} called without a receiver inside an inner class (named or 
anonymous) is resolved to the *outer* class's override, instead of the method 
the inner class inherits. Dynamic and {{@TypeChecked}} code call the inner 
class's method, as Java does.

h3. Example

{code:groovy}
@groovy.transform.CompileStatic
class C {
    String toString() { 'C' }
    def m() {
        new Object() {
            def x() { toString() }
        }.x()
    }
}

assert new C().m() != 'C' // fails: the outer class's toString() is called
{code}

Results:

||Case||dynamic||{{@TypeChecked}}||{{@CompileStatic}}||
|anonymous inner class, {{toString()}}|anonymous object|anonymous 
object|{{'C'}}|
|named inner class, {{toString()}}|inner object|inner object|{{'C'}}|
|anonymous inner class, {{hashCode()}} (outer returns 7)|identity hash|identity 
hash|{{7}}|
|closure inside an anonymous inner class, {{toString()}}|anonymous 
object|anonymous object|the enclosing closure's {{toString()}}|

When the inner class declares the method itself, the right method is called.

h3. Analysis

The type checker's method lookup on the inner class returns the outer class's 
{{toString()}} ({{C.toString()}} for receiver {{C$1}}) ahead of the 
{{Object.toString()}} that {{C$1}} inherits. The static invocation writer then 
reaches the outer instance through {{this$0}}. When the anonymous class is 
created inside a closure, {{this$0}} is that closure, which explains the last 
row.

The lookup should only fall back to outer-class methods when the inner class 
has no applicable method, inherited ones included.

h3. Notes

* Observed on master (7.0.0-SNAPSHOT, after GROOVY-12576). Not checked on 5.x 
or 6.0.x.
* Not caused by GROOVY-12576, which only changed how closures resolve these 
methods. The first three rows involve no closures.
* {{equals()}} was not tested but presumably behaves the same.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to