18

I'm just curious. Is there a way to access parent in anonymous class that is inside another anonymous class?

I make this example create a JTable subclass (anonymous class) override changeSelection and inside i create another anonymous class.

MCVE:

public class Test{

    public static void main(String args []){

        JTable table = new JTable(){

            @Override
            public void changeSelection(
                final int row, final int column,
                final boolean toggle, final boolean extend) {

                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        super.changeSelection(row, column, toggle, extend); 
                        //more code here
                    }
                });
            }
        };

    }//end main

}//end test 

How can i refer to super.changeSelection(..) ?

Roman C
  • 49,761
  • 33
  • 66
  • 176
nachokk
  • 14,363
  • 4
  • 24
  • 53
  • 3
    As a rule of thumb, when I read "outer anonymous class" I think "sounds like this is worth refactoring". Anonymous classes are great, but they can be overused. If you have classes inside other classes, consider at least defining a named inner class, if not a proper public class. It'll be easier to work with and cleaner to read, I'm willing to bet. – dimo414 Aug 05 '13 at 20:15
  • 3
    Solutions to problems of this sort are typically no more complex than "I'll create a named enclosing class." The only downside is that your codes won't all be right next to each other, but having a single reference to a class instance that is garbage collected soon after it goes out of scope offers no performance disadvantage, may allow you to use a static nested class as the enclosing class, and may actually make your implementation more concise and easier to read. – scottb Aug 05 '13 at 20:29
  • @nachokk: There are a couple of correct answers below; you should accept one of them if you find it helpful. – Jason C Aug 07 '13 at 15:06
  • @JasonC im still waiting for other answer i appreciate your help, and make upvote. – nachokk Aug 07 '13 at 16:39

4 Answers4

13

Unfortunately, you will have to give a name to the outer anonymous class:

public class Test{

    public static void main(String args []){

        class Foo extends JTable {

            @Override
            public void changeSelection(
                final int row, final int column,
                final boolean toggle, final boolean extend) {

                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        Foo.super.changeSelection(row, column, toggle, extend); 
                        //more code here
                    }
                });
            }
        };

        JTable table = new Foo();

    }//end main

}//end test 
newacct
  • 119,665
  • 29
  • 163
  • 224
  • so there is no way :( ? – nachokk Aug 05 '13 at 20:10
  • 1
    @nachokk: well, the problem is, in order to use qualified `super`, you need to have the name of the class you want the super of. But of course, an anonymous class has no name. – newacct Aug 05 '13 at 20:13
  • @nachokk No, there is no way, because as newacct says, the class is anonymous. The answers that have been posted here are the only ways to do this. You can do something like I posted, you can name the enclosing class, you can do hackery with reflection. – Jason C Aug 05 '13 at 21:15
  • You asked this on my answer, too (it's answered in those comments). You might get better results if you accept one of the answers on this question then ask a new one. The question in your original post has been answered. – Jason C Aug 06 '13 at 00:07
  • @nachokk: I believe that, if both anonymous classes were replaced with lambdas, it wouldn't work either because `this` and `super` never refers to a lambda instance; and instead refers to the closest enclosing non-lambda scope. However, I believe that if you left the outer anonymous class as an anonymous class, and changed the inner one to a lambda, then it would work with just `super`. (not tested) – newacct Aug 06 '13 at 01:39
  • i don't think the first one could be replaced by lambda, but second one cause it's an interface with one method , so your 2nd approach i was thinking – nachokk Aug 06 '13 at 01:44
4

In your context, 'super', of course, refers to the Runnable base, not the JTable base. As you know, using 'super' in an inner class refers to the superclass of that inner class, not the superclass of its enclosing class (it does not matter if it's anonymous or not). Since you want to call a method of the JTable base, you must use 'super' in the context of one of the JTable subclass methods.

You could create a new method in your JTable subclass, e.g. jTableBaseChangeSelection(), that calls the JTable's changeSelection() that you are intending to call. Then you call that from the Runnable subclass:

public static void main(String args []){

    JTable table = new JTable(){

        // calls JTable's changeSelection, for use by the Runnable inner
        // class below, which needs access to the base JTable method.
        private void jTableBaseChangeSelection (int row, int column, boolean toggle, boolean extend) {
            super.changeSelection(row, column, toggle, extend);
        }

        @Override
        public void changeSelection(
            final int row, final int column,
            final boolean toggle, final boolean extend) {

            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    // call JTable base changeSelection, since we don't have access
                    // to the JTable base class at this point.
                    jTableBaseChangeSelection(row, column, toggle, extend); 
                    //more code here
                }
            });
        }
    };

}//end main

Please note, this answer is attempting to retain your original design of an anonymous enclosing class. There are certainly reasons for doing that (and yes, quickly putting together some code is a valid reason in some cases). Having a few isolated situations where this happens -- no harm done; however, you may still wish to rethink your design if you find yourself getting into situations like this often.

Jason C
  • 38,729
  • 14
  • 126
  • 182
  • 1
    I'm unsure what you are referring to. The method `superChangeSelection` is a method of the JTable's subclass. `super` in that context refers to precisely what it should refer to. Can you clarify what you meant? – Jason C Aug 05 '13 at 16:38
  • I am also unclear on the downvote, as the answer does precisely what the OP asked. I can certainly rename that method if it is the arbitrary name that displeases you. I could rename it to `jimGarrisonChangeSelection`, perhaps? – Jason C Aug 05 '13 at 16:40
  • 1
    Jason C is correct: the "super" being used in Jason's code is JTable, which is a super-class of the anonymous JTable. But perhaps the description paragraph above could be more clear, because the Runnable base does not have a changeSelection method, and that's why the OP's code doesn't work. – Ogre Psalm33 Aug 05 '13 at 16:44
  • 1
    The way the original code was written was somewhat misleading, as `super` could refer only to the superclass of the `Runnable` instance. SO won't let me retract the downvote (why?) unless you edit the answer. – Jim Garrison Aug 05 '13 at 16:49
  • I *believe* that Jim Garrison's issue is that the name seems inappropriate in the context of being called from the Runnable subclass. I somewhat agree. However, since the method is private, and the developer knows what is going on, an explanatory comment in the code is enough to bring clarity to the code. I will edit to add a comment, and change the method to something less misleading. – Jason C Aug 05 '13 at 16:50
  • @JimGarrison Ah, I understand. The OP's code was, I believe, his non-working attempt. – Jason C Aug 05 '13 at 16:51
  • yeah i've already know this, but i was searching if exist a java syntax language to referring it , btw thanks with the answer – nachokk Aug 05 '13 at 17:17
  • You could do something weird with reflection, if you stored a reference to the JTable subclass in a variable (e.g. `anonTable`), then (may be incorrect, just typing here) `anonTable.getClass().getSuperclass().getMethod("changeSelection").invoke(anonTable, row, column, toggle, extend)`. I wouldn't recommend it. – Jason C Aug 05 '13 at 17:31
  • This is probably the best answer that will meet the letter of the question. If one needs to do this, though, why not just create a new class that extends JTable, instead of creating an anonymous extension, and then use normal Java syntax to reference it? – user1676075 Aug 05 '13 at 19:50
  • hehe i just was trying is not real code , i was curious if exist one way with java features to do that :) ... in java 8 with lambda expression can this we get accomplish? – nachokk Aug 06 '13 at 00:01
  • You cannot refer to an anonymous enclosing class's `super`. Lambda expressions are not related and do not change that rule. So, unless Java 8 added some other keyword to allow you to do this, then no. – Jason C Aug 06 '13 at 00:05
  • the runnable interface could be the one with lambda expression then you can refer when you create 'anonymous method` perhaps – nachokk Aug 06 '13 at 04:01
  • Absolutely. But then you're not trying to access 'super' from an anonymous inner class with an anonymous outer class any more, its no longer the same question. Even though you can use lambda functions to help write code that behaves equivalently, you cannot solve the problem in question with lambda functions. :-) – Jason C Aug 06 '13 at 05:43
2

I think you can create outer reference and use it in your anonymous inner. At least that works for me on JDK 1.7 and JDK 1.8.

public class Test{

        public static void main(String args []){

            class Foo extends JTable {

                @Override
                public void changeSelection(
                    final int row, final int column,
                    final boolean toggle, final boolean extend) {
                    final Foo outer = this; // reference to itself

                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            outer.changeSelection(row, column, toggle, extend); 
                            //more code here
                        }
                    });
                }
            };

            JTable table = new Foo();

        }//end main

 }//end test 
Unheilig
  • 16,196
  • 193
  • 68
  • 98
Alex Matiushkin
  • 169
  • 1
  • 3
0

I think the code below will do what is technically being asked. That said, I wouldn't recommend going this route when simpler options are available.

I'm sure you will want your run method to do something more interesting than print "Hello World!" in an infinite loop, but this seemed ok for a proof-of-concept.

package test;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.swing.JTable;
import javax.swing.SwingUtilities;

public class GetOuterAnonymousClass {

    public static void main(String args []){

        JTable table = new JTable(){

            @Override
            public void changeSelection(
                final int row, final int column,
                final boolean toggle, final boolean extend) {
                Runnable runnable = new Runnable() {
                    private Object caller;
                    public void setCaller(Object caller){
                        this.caller = caller;
                    }

                    @Override
                    public void run() {
                        System.out.println("Hello World!");
                        try {
                            Class clazz = this.getClass().getEnclosingClass();
                            Method method = clazz.getDeclaredMethod("changeSelection", new Class[]{Integer.TYPE, Integer.TYPE, Boolean.TYPE, Boolean.TYPE});
                            method.invoke(caller, 1, 1, true, true);
                        } catch (SecurityException e) {
                            e.printStackTrace();
                        } catch (NoSuchMethodException e) {
                            e.printStackTrace();
                        } catch (IllegalArgumentException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }
                    }
                };
                Method method;
                try {
                    method = runnable.getClass().getDeclaredMethod("setCaller", new Class[]{Object.class});
                    method.invoke(runnable, this);
                } catch (SecurityException e1) {
                    e1.printStackTrace();
                } catch (NoSuchMethodException e1) {
                    e1.printStackTrace();
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
                SwingUtilities.invokeLater(runnable);
            }
        };
        table.changeSelection(1, 1, true, true);

    }

}
user506069
  • 771
  • 1
  • 6
  • 14
  • 1
    That's certainly a lot of code for something that doesn't need a lot of code. In any case you can just use getClass().getEnclosingClass() to get the enclosing class directly. – Jason C Aug 05 '13 at 21:14
  • Thanks so much for your tip! Using getEnclosingClass() I was able to remove the initialization block. Thanks again! – user506069 Aug 06 '13 at 13:57