Problem with method shadowing

edited January 2018 in Questions about Code

Hi, I wrote a class named Real that represent a real number with an arbitrary precision. My final purpose is to use this class in other classes and to use the operations like add(x, y), sub(x, y), ... without having to call them by Real.add(x, y). So I moved the add(x, y) function outside my class.

Here is some made up code to help understand what I'm doing :

class Real
{
  // ... code

  void add(Real R)
  {
    this = add(this, R);
  }
}

// sketch level

Real add(Real S, Real T){/*code*/}

Now I'm happy I can add two Real anywhere in my sketch.

But :

The add(Real R) inside the class doesn't work anymore. The compiler thinks it's recursion (add not applicable for two arguments). Is there a way to access the add function that is outside the class from inside the class ?

Or is there a nicer way to do this, keeping all the code in classes ?

Thanks in advance !

Answers

  • You should really just call the functions by two different names.

    But if you want to access the sketch-level add() function from inside the Real class, you'd want to do this:

    YourSketchName.this.add();
    

    Here's a small example:

    void setup(){
     new Test().test();
    }
    
    void test(){
      println("here");
    }
    
    class Test{
     void test(){
       MySketch.this.test();  
     }
    }
    

    But like I said, this is probably a bad idea. Just rename the functions so it's not confusing.

  • edited January 2018

    Say I'm stubborn and I don't want to change the names of the functions, how could I improve your code so I don't have to update the sketch's name in each sketch I use my class ?

    Here is a start, but the commented line throws a NoSuchMethodException, and I can't figure out why.

    final Class sketch = getClass();
    
    void setup(){
      printArray(sketch.getDeclaredMethods());
      new Test().test();
    }
    
    void test(){
      println("here");
    }
    
    class Test{
     void test(){
       //sketch.getMethod("test", null).invoke(sketch, null);
     }
    }
    

    getMethod() documentation

    invoke() documentation

    And your code does what I want, however I don't understand why MySketch.this.test() refers to the sketch-level test() function. I understand that here, this refers to the Test class so the test() called is the one in the Test class.

    Can you clarify this ?

  • When you're in the Test class and call a function, it first looks in the Test class for a function with that name. If it can't find one, then it looks at the sketch-level functions. Using SketchName.this.test() tells it to skip over the class-level and just call the sketch-level function directly.

    Another approach would be to get a reference to the sketch, something like this:

    final MySketch sketch = this;
    
    void setup(){
      new Test().test();
    }
    
    void test(){
      println("here");
    }
    
    class Test{
     void test(){
       sketch.test();
     }
    }
    

    But honestly, this is pretty hacky. Don't do this. Just rename the method.

  • Thanks for your help ! I'll follow your advice and just rename the method.

  • It's also worth noting that this is not method overloading. Method overloading happens when you have two versions of the same function in the same scope that take different parameters:

    class Test{
      void test(){
        //do a thing
      }
    
      void test(String example){
        //do a different thing
      }
    }
    

    Method overriding is when you have a subclass that contains a function with the same name:

    class Test{
      void test(){
        //do a thing
      }
    }
    
    class TestSubclass extends Test{
      void test(){
        //do a different thing
      }
    }
    

    What you're doing is called method shadowing and as you've seen, it means that methods in the inner class scope are called instead of methods in the outer scope. More info here: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html

  • If you wish your inner class to automatically find out its enclosing PApplet class' reference, try this code out: :ar!

    /**
     * Inner this$0 (v1.0)
     * GoToLoop (2018/Jan/05)
     *
     * Forum.Processing.org/two/discussion/25824/
     * problem-with-method-overloading#Item_6
     */
    
    void setup() {
      new Inner();
      exit();
    }
    
    class Inner {
      Inner() {
        println(getEnclosingPApplet() instanceof PApplet);
      }
    
      protected PApplet getEnclosingPApplet() {
        try {
          return (PApplet) getClass().getDeclaredField("this$0").get(this);
        }
    
        catch (final ReflectiveOperationException cause) {
          throw new RuntimeException(cause);
        }
      }
    }
    
  • That's even hackier than just doing MySketch sketch = this;. None of this should actually be used.

  • How do I trigger a sketch-level method from getEnclosingPApplet() then ?

  • You would have to cast it to MySketch or whatever. This isn't an improvement.

  • without having to call them by Real.add(x, y).

    Well, that's wrong in the first place.

    You want x.add(y); - much shorter.

    I wouldn't recommend to move add() out of the class at all. Just stick to the class and put as much as you can inside the class.

  • edited January 2018

    How do I trigger a sketch-level method from getEnclosingPApplet() then?

    Dunno what you mean by "trigger". But if you've simply meant for an inner class to access members of our sketch via "this$0", we can't by regular means. [-X

    Only by some hackish workarounds like 100% reflection or Java's bundled JS Nashorn. :ar!

    The reason why we can't is b/c we dunno the name of the enclosing top-class under Processing's IDE (PDE); which is the name of our sketch's folder btW. @-)

    Therefore, whatever name is chosen when we save our sketch becomes the datatype of the enclosing PApplet top-subclass. :-B

    Regardless, an inner class already has full access to its enclosing class members and vice-versa; as long as the inner class doesn't overshadow an enclosing member's name. :-\"

    What I don't understand is why are you attempting to create a library using an inner class! :-/

    You should at least create a ".java" tab and put your library class there! *-:)

  • Pay attention to what koogs just posted. I meant to post that in my original answer.

    Basically, what you're trying to come up with has already been developed. Just use the existing BigDecimal class.

Sign In or Register to comment.