Error while redefining a method with ByteBuddy: "class redefinition failed: attempted to add a method"
<p>I'm learning Byte Buddy and I'm trying to do the following:</p>

<ul>
<li>create a subclass from a given class or interface</li>
<li>then replace a method in the subclass</li>
</ul>

<p>Note that the subclass is 'loaded' in a <code>ClassLoader</code> <em>before</em> one of its method (<code>sayHello</code>) is redefined. It fails with the following error message:</p>

<pre><code>java.lang.UnsupportedOperationException: class redefinition failed: attempted to add a method
at sun.instrument.InstrumentationImpl.redefineClasses0(Native Method)
at sun.instrument.InstrumentationImpl.redefineClasses(InstrumentationImpl.java:170)
at net.bytebuddy.dynamic.loading.ClassReloadingStrategy$Strategy$1.apply(ClassReloadingStrategy.java:293)
at net.bytebuddy.dynamic.loading.ClassReloadingStrategy.load(ClassReloadingStrategy.java:173)
...
</code></pre>

<p>Below is the code for a set of JUnit tests. The first test, <code>shouldReplaceMethodFromClass</code>, passes as the <code>Bar</code> class is not subclassed before redefining its method. The two other tests fail when the given <code>Bar</code> class or <code>Foo</code> interface is subclassed.</p>

<p>I read that I should delegate the new method in a separate class, which is what I do using the <code>CustomInterceptor</code> class, and I also installed the ByteBuddy agent at the test startup and used to load the subclass, but even with that, I'm still missing something, and I can't see what :(</p>

<p>Anyone has an idea ?</p>

<pre><code>public class ByteBuddyReplaceMethodInClassTest {

  private File classDir;

  private ByteBuddy bytebuddy;

  @BeforeClass
  public static void setupByteBuddyAgent() {
    ByteBuddyAgent.install();
  }

  @Before
  public void setupTest() throws IOException {
    this.classDir = Files.createTempDirectory("test").toFile();
    this.bytebuddy = new ByteBuddy().with(Implementation.Context.Disabled.Factory.INSTANCE);
  }

  @Test
  public void shouldReplaceMethodFromClass()
      throws InstantiationException, IllegalAccessException, Exception {
    // given
    final Class&lt;? extends Bar&gt; modifiedClass = replaceMethodInClass(Bar.class,
        ClassFileLocator.ForClassLoader.of(Bar.class.getClassLoader()));
    // when
    final String hello = modifiedClass.newInstance().sayHello();
    // then
    assertThat(hello).isEqualTo("Hello!");
  }

  @Test
  public void shouldReplaceMethodFromSubclass()
      throws InstantiationException, IllegalAccessException, Exception {
    // given
    final Class&lt;? extends Bar&gt; modifiedClass = replaceMethodInClass(createSubclass(Bar.class),
        new ClassFileLocator.ForFolder(this.classDir));
    // when
    final String hello = modifiedClass.newInstance().sayHello();
    // then
    assertThat(hello).isEqualTo("Hello!");
  }

  @Test
  public void shouldReplaceMethodFromInterface()
      throws InstantiationException, IllegalAccessException, Exception {
    // given
    final Class&lt;? extends Foo&gt; modifiedClass = replaceMethodInClass(createSubclass(Foo.class),
        new ClassFileLocator.ForFolder(this.classDir));
    // when
    final String hello = modifiedClass.newInstance().sayHello();
    // then
    assertThat(hello).isEqualTo("Hello!");
  }


  @SuppressWarnings("unchecked")
  private &lt;T&gt; Class&lt;T&gt; createSubclass(final Class&lt;T&gt; baseClass) {
    final Builder&lt;T&gt; subclass =
        this.bytebuddy.subclass(baseClass);
    final Loaded&lt;T&gt; loaded =
        subclass.make().load(ByteBuddyReplaceMethodInClassTest.class.getClassLoader(),
            ClassReloadingStrategy.fromInstalledAgent());
    try {
      loaded.saveIn(this.classDir);
      return (Class&lt;T&gt;) loaded.getLoaded();
    } catch (IOException e) {
      throw new RuntimeException("Failed to save subclass in a temporary directory", e);
    }
  }

  private &lt;T&gt; Class&lt;? extends T&gt; replaceMethodInClass(final Class&lt;T&gt; subclass,
      final ClassFileLocator classFileLocator) throws IOException {
    final Builder&lt;? extends T&gt; rebasedClassBuilder =
        this.bytebuddy.redefine(subclass, classFileLocator);
    return rebasedClassBuilder.method(ElementMatchers.named("sayHello"))
        .intercept(MethodDelegation.to(CustomInterceptor.class)).make()
        .load(ByteBuddyReplaceMethodInClassTest.class.getClassLoader(),
            ClassReloadingStrategy.fromInstalledAgent())
        .getLoaded();
  }

  static class CustomInterceptor {
    public static String intercept() {
      return "Hello!";
    }
  }


}
</code></pre>

<p>The <code>Foo</code> interface and <code>Bar</code> class are:</p>

<pre><code>public interface Foo {

    public String sayHello();

}
</code></pre>

<p>and </p>

<pre><code>public class Bar {

    public String sayHello() throws Exception {
      return null;
    }

}
</code></pre>