4

I have the following (third-party) class structure. We'll call the third-party project ProjectSeriously, and note that I'm using System.out.println in place of other complicated functionality (100s of lines of code).

class A {
    public void hi() {
        // Do an important thing
        System.out.println("Important thing A");
    }
}

class B extends A { 
    public void hi() {
        // Do some terrible, terrible things
        System.out.println("TERRIBLE THING B");

        // Do that important thing
        super.hi();
    }
}

Now I want to write this (this isn't valid java):

class C extends B { 
    public void hi() {
        // Do some not-so-terrible things
        System.out.println("Ok thing C");

        // Do that important thing
        super.super.hi();
    }
}

I have to pass an instanceof B to some other piece of this wonderful project, ProjectSeriously. Seeing as these are public methods, I feel like this should be possible.

Cory Kendall
  • 7,195
  • 8
  • 37
  • 64
  • 6
    see [Why is super.super.method(); not allowed in Java?](http://stackoverflow.com/q/586363/217324) – Nathan Hughes Apr 28 '15 at 19:54
  • http://stackoverflow.com/questions/586363/why-is-super-super-method-not-allowed-in-java would suggest you are on mission impossible ... – J Richard Snape Apr 28 '15 at 19:56
  • If you really, really need to do this, see whether [this answer using reflection](http://stackoverflow.com/a/2692379/1798593) will work for your case. – Patricia Shanahan Apr 28 '15 at 20:05

4 Answers4

3

You could use javassist to modify the class before any use of it.

But this is a really ugly hack, please try to refactor the code in A and/or B the expose the important parts.

package test;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;

class A {
    public void hi() {
        // Do an important thing
        System.out.println("Important thing A");
    }
}

class B extends A { 
    public void hi() {
        // Do some terrible, terrible things
        System.out.println("TERRIBLE THING B");

        // Do that important thing
        super.hi();
    }
}

class C extends B { 
    public void hi() {
        // Do some not-so-terrible things
        System.out.println("Ok thing C");

        // Do that important thing
        super.hi();
    }
}

public class Main {

    public static void main(String[] args) throws Exception {
        CtClass cc = ClassPool.getDefault().get("test.B"); // don't use test.B.class.getName() as this force the class loader to load the class
        CtMethod m1 = cc.getDeclaredMethod("hi");
        cc.removeMethod(m1);
        CtMethod m2 = CtNewMethod.copy(m1, cc, null);
        m2.setBody("{ /* override method B.hi() body */ return super.hi();}", "this", m1.getName());
        cc.addMethod(m2);
        cc.toClass();
        C obj = new C();
        obj.hi();
    }
}

Result:

Ok thing C
Important thing A
0

Unless you explicitly expose the method(s), which sort-of goes against the design pattern, there aren't many other options:

public class GrandParent {
    public void hi() {
        hiGrandParent();
    }

    protected final void hiGrandParent() {
        System.out.println("Hi from grandparent.");
    }

    public static class Parent extends GrandParent {
        @Override
        public void hi() {
            hiParent();
        }

        protected final void hiParent() {
            System.out.println("Hi from parent.");
        }
    }

    public static class Child extends Parent {
        @Override
        public void hi() {
            hiChild();
            super.hi();
            hiParent();
            hiGrandParent();
        }

        protected final void hiChild() {
            System.out.println("Hi from child.");
        }
    }
}

Run with:

public final class RunIt {

    public static void main(final String[] args) {
        new GrandParent.Child().hi();
    }
}

Expected output:

Hi from child.
Hi from parent.
Hi from parent.
Hi from grandparent.
CasualT
  • 4,869
  • 1
  • 31
  • 53
0

This would break encapsulation in a terrible way (you'd essentially be disabling some part of class B's logic), and it shouldn't be possible in Java. I'm pretty sure it isn't possible.

olovb
  • 2,164
  • 1
  • 17
  • 20
0

Yeah its not really possible in a "standard" java way, also its a bad design decision but OP might not have access to the original class. I've faced this problem several times earlier with different jars.

If you want to skip for example a private method call in one of the super classes, but still need the other parts of the constructor code or the functionality of the superclasses, the only "easy" way to do this is to basically copy-paste that part of the code into your own class. For example if you have these classes:

public class Foo {

    public Foo() {
        importantFunctionality();
    }

    private void importantFunctionality() {
        System.out.println("DOING IMPORTANT STUFF");
    }

}

public class Bar extends Foo {

    public Bar() {
        super(); //constructor gets called
        killAllBabies(); //I dont want this to get called, but its a private method meaning no overriding
        solveWorldHunger(); //I want to call this, but this is a private method, so no calling this from child classes
    }

    private void killAllBabies() {
        System.out.println("KILLING ALL BABIES");
    }

    private void solveWorldHunger() {
        System.out.println("SOLVING WORLD HUNGER");
    }

}

public class MyClass extends Bar {

    public MyClass() {
        super(); //Not good, because stuff I dont want gets called here
    }

}

Only way to solve this is to "skip" the previous class and extend the original class and implement the functionality of the skipped class. Unfortunately this was an issue for us with a certain framework because of bad extendibility:

public class MyClass extends Foo {

    public MyClass() {
        super();
        solveWorldHunger();
    }

    private void solveWorldHunger() {
        System.out.println("SOLVING WORLD HUNGER");
    }

}
breakline
  • 5,776
  • 8
  • 45
  • 84