4

I want to replace a function of a class where I know the source code. The problem is that I cannot override the function because it's private and also I cannot extend the class and override the whole functionallity because the bad calles are in every possible constructor.

Now I'm totally lost I don't know how I can implement that desired functionallity (theming the controls of the Android VideoView controls aka MediaController). Is there any way to replace a function with reflection or something like that? Or even is it possible not to call the construtor of the super class but the super super class?

Here is a very basic class which shows my problem:

public class Base {
    public Base(int state) {
        // must be called
    }
}
public class Example extends Base {
    public Example(int state, boolean bla) {
        super(state);
        badCall();
    }        
    public Example(int state) {
        this(state, false);
    }
    private badCall() {
        // doing bad things
    }
}

Also note that I need to have an instance of Example so I cannot simply copy the class.

rekire
  • 47,260
  • 30
  • 167
  • 264
  • Can you "reverse the effects" of "badCall()" after the constructor has run? Ugly, but maybe the solution with the least "fluff". Or maybe (I'm not experienced with AOP, so not sure...) it is possible to do it with aspect oriented programming, and intercepting the call. – ppeterka Jan 03 '13 at 08:08
  • No I cannot "reverse the effects". It's about the play/pause button in the MediaController where I see no way to observe changes. @assylias is it possible to to use that in android too? I don't know that classes/framework. – rekire Jan 03 '13 at 08:09
  • Can't you rewrite the second constructor (with (int state)) to call directly super(state) and then this.bla=false? – George D Jan 03 '13 at 08:13
  • @GeorgeD No I cannot rewrite the `Example` class be cause it's a part of android / a closed framework. – rekire Jan 03 '13 at 08:15

3 Answers3

2

Here is a contrived example using a mocking framework (jmockit). The output is:

Original private method
Some other method
New private method
Some other method

public static void main(String[] args) {
    A a = new A(); //prints Original private method
    a.m2(); //prints Some other method

    new MockUp<A>() {

        @Mock
        public void m() {
            System.out.println("New private method");
        }
    };

    A b = new A(); //prints New private method
    b.m2(); //prints Some other method
}

static class A {

    public A() {
        m();
    }

    private void m() {
        System.out.println("Original private method");
    }

    public void m2() {
        System.out.println("Some other method");
    }
}
assylias
  • 321,522
  • 82
  • 660
  • 783
  • @rekire I don't know to be honest. jmockit is a jar so you can try to add it to your project's libraries and see if it works. If it does not there are [several mocking libraries available on android](http://stackoverflow.com/questions/3337505/mocking-library-framework-that-works-best-in-android) which probably have similar capabilities. – assylias Jan 03 '13 at 08:25
  • @rekire jmockit is apparently not supported on android: https://groups.google.com/forum/?fromgroups=#!topic/jmockit-users/k0QrQ64KZ9g – assylias Jan 03 '13 at 08:29
2

You could use AspectJ for accessing private methods:

public privileged aspect SomeClassExposerAspect {

  void around(SomeClass obj) : execution(private void badCall()) {        
       try {
          // use privileged access to return the private member
          ((Example )obj).badCall();
       }
       // safeguard against unexpected 3rd party library versions, if using load-time weaving
       catch (NoSuchMethodError e) {
          //log something
       }
   }
  }
}

Here is a nice blog about it.

asgoth
  • 35,552
  • 12
  • 89
  • 98
  • This looks good for a pure java project. But my project runs on android. Thank you anyway. I think this meight be helpful for others. – rekire Jan 03 '13 at 08:27
  • It is possible to use [AspectJ on Android](http://blog.punegtug.org/2010/11/adding-aspect-to-android.html). – asgoth Jan 03 '13 at 08:36
  • That seems to be very complicated if I got it right I need to change the build system where I have no experiance and that scares me a bit. – rekire Jan 03 '13 at 08:48
1

If you have the source I suggest you "patch" the class.

Compile and build your modified version and replace the old version in the jar or add it earlier in the class path so it hides the old version.

BTW: You can even patch classes in the JVM this way, though it is rarely a good idea. ;)

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130