override java final methods via reflection or other means?

前端 未结 6 1654
醉梦人生
醉梦人生 2020-12-15 09:28

This question arise while trying to write test cases. Foo is a class within the framework library which I dont have source access to.

public class Foo{
  pub         


        
6条回答
  •  暖寄归人
    2020-12-15 09:40

    As this was one of the top results for "override final method java" in google. I thought I would leave my solution. This class shows a simple solution using the example "Bagel" class and a free to use javassist library:

    /**
     * This class shows how you can override a final method of a super class using the Javassist's bytecode toolkit
     * The library can be found here: http://jboss-javassist.github.io/javassist/
     * 
     * The basic idea is that you get the super class and reset the modifiers so the modifiers of the method don't include final.
     * Then you add in a new method to the sub class which overrides the now non final method of the super class.
     * 
     * The only "catch" is you have to do the class manipulation before any calls to the class happen in your code. So put the
     * manipulation as early in your code as you can otherwise you will get exceptions.
     */
    
    package packagename;
    
    import javassist.ClassPool;
    import javassist.CtClass;
    import javassist.CtMethod;
    import javassist.CtNewMethod;
    import javassist.Modifier;
    
    /** 
     * A simple class to show how to use the library
     */
    public class TestCt {
    
        /** 
         * The starting point for the application
         */
        public static void main(String[] args) {
    
            // in order for us to override the final method we must manipulate the class using the Javassist library.
            // we need to do this FIRST because once we initialize the class it will no longer be editable.
            try
            {
                // get the super class
                CtClass bagel = ClassPool.getDefault().get("packagename.TestCt$Bagel");
    
                // get the method you want to override
                CtMethod originalMethod = bagel.getDeclaredMethod("getDescription");
    
                // set the modifier. This will remove the 'final' modifier from the method.
                // If for whatever reason you needed more than one modifier just add them together
                originalMethod.setModifiers(Modifier.PUBLIC);
    
                // save the changes to the super class
                bagel.toClass();
    
                // get the subclass
                CtClass bagelsolver = ClassPool.getDefault().get("packagename.TestCt$BagelWithOptions");
    
                // create the method that will override the super class's method and include the options in the output
                CtMethod overrideMethod = CtNewMethod.make("public String getDescription() { return super.getDescription() + \" with \" + getOptions(); }", bagelsolver);
    
                // add the new method to the sub class
                bagelsolver.addMethod(overrideMethod);
    
                // save the changes to the sub class
                bagelsolver.toClass();
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
    
            // now that we have edited the classes with the new methods, we can create an instance and see if it worked
    
            // create a new instance of BagelWithOptions
            BagelWithOptions myBagel = new BagelWithOptions();
    
            // give it some options
            myBagel.setOptions("cheese, bacon and eggs");
    
            // print the description of the bagel to the console.
            // This should now use our new code when calling getDescription() which will include the options in the output.
            System.out.println("My bagel is: " + myBagel.getDescription());
    
            // The output should be:
            // **My bagel is: a plain bagel with cheese, bacon and eggs**
        }
    
        /**
         * A plain bagel class which has a final method which we want to override
         */
        public static class Bagel {
    
            /**
             * return a description for this bagel
             */
            public final String getDescription() {
                return "a plain bagel";
            }
        }
    
        /**
         * A sub class of bagel which adds some extra options for the bagel.
         */
        public static class BagelWithOptions extends Bagel {
    
            /**
             * A string that will contain any extra options for the bagel
             */
            String  options;
    
            /**
             * Initiate the bagel with no extra options
             */
            public BagelWithOptions() {
                options = "nothing else";
            }
    
            /**
             * Set the options for the bagel
             * @param options - a string with the new options for this bagel
             */
            public void setOptions(String options) {
                this.options = options;
            }
    
            /**
             * return the current options for this bagel
             */
            public String getOptions() {
                return options;
            }
        }
    }
    

提交回复
热议问题