groovy
  1. groovy
  2. GROOVY-3493

Cannot override methods via metaclass that are part of an interface implementation

    Details

    • Type: Bug Bug
    • Status: Open Open
    • Priority: Critical Critical
    • Resolution: Unresolved
    • Affects Version/s: 1.6.2
    • Fix Version/s: None
    • Component/s: groovy-runtime
    • Labels:
      None
    • Number of attachments :
      0

      Description

      The following works...

      class T {
             def doIt() { true }
      }
      
      def t = new T()
      
      assert t.doIt()
      t.metaClass.doIt = { -> false }
      assert !t.doIt()
      

      But this fails...

      interface I {
             def doIt()
      }
      
      class T implements I {
             def doIt() { true }
      }
      
      def t = new T()
      
      assert t.doIt()
      t.metaClass.doIt = { -> false }
      assert !t.doIt()
      

        Issue Links

          Activity

          Hide
          Slava Schmidt added a comment -

          I can also reproduce this at Mac OS x with Groovy Version: 1.6.3 and JVM: 1.6.0_13 installed

          Show
          Slava Schmidt added a comment - I can also reproduce this at Mac OS x with Groovy Version: 1.6.3 and JVM: 1.6.0_13 installed
          Hide
          Todd Greenwood added a comment - - edited

          Adding repro steps for java classes.
          //--------------------------------------------
          // REPRO 2: Override a java class instance...should work, and does
          //--------------------------------------------

          #JavaClass.java
          public class JavaClass
          {
              public Object doSomething(Object o)
              {
                  throw new IllegalStateException( "This method should be overriden");
              }
          }
          
          #MyScript.groovy
          class Dog
          {
              String name;
          }
          
          JavaClass jc = new JavaClass();
          jc.metaClass.doSomething = { Object o ->
              println o;
              return o;
          }
          def dog = new Dog().name="fido";
          assert(jc.doSomething(dog).equals(dog));
          

          //--------------------------------------------
          // REPRO 3: Override a java class instance
          // based on an interface...this does not work
          //--------------------------------------------

          #IWorker.java
          public interface IWorker
          {
              public Object doSomething(Object o);
          }
          
          #LazyWorker.java
          public class LazyWorker implements IWorker
          {
              @Override
              public Object doSomething( final Object o )
              {
                  throw new IllegalStateException( "should be overridden");
              }
          }
          
          #MyScript.groovy
          IWorker lw = new LazyWorker();
          lw.metaClass.doSomething = { Object o ->
              println o;
              return o;
          }
          
          assert(lw.doSomething(dog).equals(dog));
          
          ERROR:
          The metaClass override is ignored, and the IllegalStateException from the original impl is used.
          
          Show
          Todd Greenwood added a comment - - edited Adding repro steps for java classes. //-------------------------------------------- // REPRO 2: Override a java class instance...should work, and does //-------------------------------------------- #JavaClass.java public class JavaClass { public Object doSomething( Object o) { throw new IllegalStateException( "This method should be overriden" ); } } #MyScript.groovy class Dog { String name; } JavaClass jc = new JavaClass(); jc.metaClass.doSomething = { Object o -> println o; return o; } def dog = new Dog().name= "fido" ; assert (jc.doSomething(dog).equals(dog)); //-------------------------------------------- // REPRO 3: Override a java class instance // based on an interface...this does not work //-------------------------------------------- #IWorker.java public interface IWorker { public Object doSomething( Object o); } #LazyWorker.java public class LazyWorker implements IWorker { @Override public Object doSomething( final Object o ) { throw new IllegalStateException( "should be overridden" ); } } #MyScript.groovy IWorker lw = new LazyWorker(); lw.metaClass.doSomething = { Object o -> println o; return o; } assert (lw.doSomething(dog).equals(dog)); ERROR: The metaClass override is ignored, and the IllegalStateException from the original impl is used.
          Hide
          Paul King added a comment - - edited

          Checking into the code which causes this (around line 377 of MetaMethodIndex.java) there was special handling put in (and then tweaked) to not overwrite interface methods with instance methods in certain circumstances - e.g. to stop wierd behavior when proxies are in play. Having said that, I am not convinced that the corrent behavior is what we really want. A related issue in the past is GROOVY-2391.

          Show
          Paul King added a comment - - edited Checking into the code which causes this (around line 377 of MetaMethodIndex.java) there was special handling put in (and then tweaked) to not overwrite interface methods with instance methods in certain circumstances - e.g. to stop wierd behavior when proxies are in play. Having said that, I am not convinced that the corrent behavior is what we really want. A related issue in the past is GROOVY-2391 .
          Roshan Dawrani made changes -
          Field Original Value New Value
          Link This issue is duplicated by GROOVY-4210 [ GROOVY-4210 ]
          Hide
          Tim Myer added a comment - - edited

          I have gotten this test case to pass by modifying lines 376 and 377 of MetaMethodIndex.java to the following:

          if (match.isPrivate()
            || (!isNonRealMethod(match) && match.getDeclaringClass().isInterface() && !method.getDeclaringClass().isInterface() && !(method instanceof ClosureMetaMethod))) {
          

          I am not entirely sure what other side effects this will have, but for me, it appears to work in the simple case.

          Show
          Tim Myer added a comment - - edited I have gotten this test case to pass by modifying lines 376 and 377 of MetaMethodIndex.java to the following: if (match.isPrivate() || (!isNonRealMethod(match) && match.getDeclaringClass().isInterface() && !method.getDeclaringClass().isInterface() && !(method instanceof ClosureMetaMethod))) { I am not entirely sure what other side effects this will have, but for me, it appears to work in the simple case.
          Hide
          Graeme Rocher added a comment -

          I'm assigning this issue as I think it has enough votes and is serious enough to warrant looking at

          Show
          Graeme Rocher added a comment - I'm assigning this issue as I think it has enough votes and is serious enough to warrant looking at
          Graeme Rocher made changes -
          Fix Version/s 1.8.x [ 15750 ]
          Hide
          David Dawson added a comment -

          It'd also have to handle ClosureStaticMetaMethod.

          Show
          David Dawson added a comment - It'd also have to handle ClosureStaticMetaMethod.
          Hide
          thurston n added a comment -

          What is the status of this bug?
          Is still present in 1.8.4

          Show
          thurston n added a comment - What is the status of this bug? Is still present in 1.8.4
          Hide
          cow like added a comment -

          Bug still present in 2.0.1

          This passes in the console:

          interface IFoo

          { int foo(); }

          class IFooImpl implements IFoo { int foo()

          { 1 }

          }
          IFoo.metaClass.foo =

          { -> 2 }

          // changing to IFooImpl.metaClass fails
          assert new IFooImpl().foo() == 2

          Show
          cow like added a comment - Bug still present in 2.0.1 This passes in the console: interface IFoo { int foo(); } class IFooImpl implements IFoo { int foo() { 1 } } IFoo.metaClass.foo = { -> 2 } // changing to IFooImpl.metaClass fails assert new IFooImpl().foo() == 2
          Hide
          jorge fiallega added a comment - - edited

          In the meantime, what is the recommended way to mock methods in classes that implement and interface.
          Like in the given example how do we mock doIt so that we may test another methods that uses it ?
          I naively thought that using MockFor would get rid of the problem, but I get the same behavior.

          Show
          jorge fiallega added a comment - - edited In the meantime, what is the recommended way to mock methods in classes that implement and interface. Like in the given example how do we mock doIt so that we may test another methods that uses it ? I naively thought that using MockFor would get rid of the problem, but I get the same behavior.
          Hide
          Alex Anderson added a comment -

          Would also appreciate a recommended workaround.

          Show
          Alex Anderson added a comment - Would also appreciate a recommended workaround.
          Pascal Schumacher made changes -
          Fix Version/s 1.8.x [ 15750 ]
          blackdrag blackdrag made changes -
          Component/s groovy-runtime [ 16250 ]

            People

            • Assignee:
              Unassigned
              Reporter:
              Luke Daley
            • Votes:
              26 Vote for this issue
              Watchers:
              24 Start watching this issue

              Dates

              • Created:
                Updated: