Ошибки при изменении существующего класса с использованием JavaAssist

Я работаю над следующей задачей CodeWars:

https://www.codewars.com/kata/hack-22/train/java

Вот что я написал:

public static Yossarian loophole() throws Throwable {
    ClassPool pool = ClassPool.getDefault();
    //Loader cl = new Loader(pool);
    CtClass yossarianClass = pool.get("Yossarian");
    int modifiers = yossarianClass.getDeclaredMethod("isCrazy").getModifiers();
    if(Modifier.isFinal(modifiers)) {
        System.out.println("Removing Final");
        int notFinalModifier = Modifier.clear(modifiers, Modifier.FINAL);
        yossarianClass.getDeclaredMethod("isCrazy").setModifiers(notFinalModifier);
        yossarianClass.rebuildClassFile();
    }
    final CtClass saneYossarianClass = ClassPool.getDefault().makeClass("SaneYossarian");
    saneYossarianClass.setSuperclass(yossarianClass);
    final CtMethod overrideMethod = CtNewMethod.make("public boolean isCrazy() { return true; }", saneYossarianClass);
    saneYossarianClass.addMethod(overrideMethod);
    final Class<?> aClass = saneYossarianClass.toClass(Yossarian.class.getClassLoader(), Yossarian.class.getProtectionDomain());
    return (Yossarian) aClass.newInstance();
}

и я получаю следующую ошибку:

Исключение в потоке "main" javassist.CannotCompileException: by java.lang.ClassFormatError: класс SaneYossarian переопределяет последний метод isCrazy.()Z

Что не имеет смысла! Я использовал JavaAssist точно, чтобы изменить исходный класс. Я не ищу решения проблемы, просто понимаю, что я сделал неправильно на этапе, когда я изменил класс. Любая помощь приветствуется.

ОБНОВЛЕНИЕ: я также попытался напрямую изменить метод в базовом классе, чтобы вернуть true,

1 ответ

Вместо

final Class<?> aClass = saneYossarianClass.toClass(Yossarian.class.getClassLoader(), Yossarian.class.getProtectionDomain());

использование

 final Class<?> aClass = saneYossarianClass.toClass();

По некоторым причинам это работает для меня:

public final class SomeClassHavingFinals {

    public final void sayHelloBoy() {
        System.out.println("I am saying hello original");
    }
}

А также:

public static void main(String[] args)
            throws NotFoundException, CannotCompileException, InstantiationException, IllegalAccessException,
            NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException, IOException {
        ClassPool pool = ClassPool.getDefault();
        CtClass clazz = pool.get("testapp.SomeClassHavingFinals");
        clazz.defrost();
        clazz.setModifiers(Modifier.PUBLIC);
        clazz.getDeclaredMethod("sayHelloBoy").setModifiers(Modifier.PUBLIC);
        clazz.toClass();

        CtClass extension = pool.makeClass("SomeExtension");
        extension.setSuperclass(clazz);
        final CtMethod overrideMethod = CtNewMethod.make("public void sayHelloBoy() { System.out.println(\"Im overriden\"); }",
                extension);
        extension.addMethod(overrideMethod);
        Object ei = extension.toClass().newInstance();
        ei.getClass().getDeclaredMethod("sayHelloBoy").invoke(ei);
    }

выходы

Я переопределен

Похоже, это работает. Внутренние IDK, но кажется, что toClass() вызывает переопределение определения файла.

Другие вопросы по тегам