1

我正在处理以下 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();
}

我收到以下错误:

线程“主”javassist.CannotCompileException 中的异常:由 java.lang.ClassFormatError:类 SaneYossarian 覆盖最终方法 isCrazy.()Z

这没有任何意义!我使用 JavaAssist 正是为了修改原始类。我不是在寻找挑战的解决方案,只是了解我在修改课程的步骤中做错了什么。任何帮助表示赞赏。

更新:我也尝试直接修改基类中的方法返回true,

4

1 回答 1

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() 导致文件定义被重建。

于 2019-02-02T00:41:53.933 回答