Base class with protected method (I do not know a good topic title for this, sorry)

Good day,

I have an abstract class from which I derive other classes.

abstract class GuiControl
{
  GuiControl(int x, int y, int w, int h, String text)
  {
    ...
    ...
  }
  ...
  ...
  /*
  Test if mouse is over control
   Returns:
   true if it is, else false
   */
  protected boolean testMouseposition()
  {
    // return value
    boolean rb = false;

    // if the mouse is on the control
    if (mouseX > this.ctrlX && mouseX < this.ctrlX + this.ctrlW &&
      mouseY > this.ctrlY && mouseY < this.ctrlY + this.ctrlH)
    {
      rb = true;
    }

    return rb;
  }
}

In a derived class I can access the above method.

class GuiForm extends GuiControl
{
  boolean hasFocus  false;

  GuiForm(int x, int y, int w, int h, String text, PFont font)
  {
    super(x, y, w, h, text);
    this.font = font;
  }
  ...
  ...

  public boolean onClick()
  {
    hasFocus = testMousePosition();
    return hasFocus;
  }
}

I would like to prevent an instance of the derived class to access the testMouseposition() method as there is no need for it.


GuiForm myForm;

void setup()
{
  myForm = new GuiForm(...);
  myForm.testMouseposition();  // <<<< this here should not be possible
}

void draw()
{
  ...
  ...
}

Can it be done? If so, what to read up about. I canā€™t use private instead of protected as the derived class cant see it. I can (obviously) move testMouseposition() to the derived classes but that means that I have to implement the exact same method in all derived classes which is extra work and, in my view, defeats the purpose of the whole OOP approach.

Is this a flaw in my thinking about the implementation?

Thanks in advance.

Note:
Please no comments on the fact that Iā€™m re-inventing the wheel bui writing some GUI code; this started as a learning exercise. I had a quick look at controlP5 and instead of spending time on understanding it I decided to rather spend time on learning Processing / Java :wink:

1 Like

tbh I donā€™t think thatā€™s possible in processing.

Reason is that processing uses a pre-compiler that writes all your classes into one big class and then compiles.

Hence, all the classes become sub-classes

1 Like

Everything inside .pde files are concatenated as 1 big PApplet subclass.

Access keywords like protected or private canā€™t disallow PApplet methods such as setup() or draw() from accessing members from inner classes within a ā€œ.pdeā€ file.

So youā€™re gonna need to move its class to a separate ā€œ.javaā€ file.

1 Like

Thanks people for the replies.

@Chrisir, I have a vague understanding how it works (Arduino does, not quite surprisingly, basically the same when using multiple pde/ino files).

@GoToLoop, I did not know that I could add java files in Processingā€™s IDE. But a basic test seems to show that I can. Will work that out later.

Some sketches w/ ā€œ.pdeā€ + ā€œ.javaā€ files:

1 Like

It seems to me that you want all GUI classes to inherit from your GuiControl class but only some of them to be able to see the testMouseposition method.

The solution is straightforward

  1. Create a new abstract class called GuiControlMouse that extends the GuiControl class (the actual name is not important)
  2. Move the testMouseposition method from GuiControl to GuiControlMouse

The GuiForm class is unchanged but can no longer see the testMouseposition method. Any classes that need to use this method should extend the GuiControlMouse

1 Like

Thanks for the reply. All GUI classes should see the testMousePosition() and use it internally. But the instances of those classes should not be able to see / use it.

I have a GuiForm, a GuiButton and a GuiTextbox class at this moment, all deriving from GuiControl. A form instance can contain instances of the other two (and actually of a GuiForm as well (so nested forms)). Each derived class above has a public onClick() method that calls the testMousePosition() so the user application e.g. can set the focus on a textbox or execute a method associated with a button when clicked. The user application has no need to know about testMousePosition(), only about onClick().

I now understand what you you want to do :grin: unfortunately its not possible in Processing.

When you create a basic sketch, Processing stores your code in one or more .pde files (one per tab). When you execute the sketch Processing combines all the pde files into one and creates a Java class for the code, the name of the class being the name of the sketch.

If you create a class inside a pde file it becomes an inner class and all attributes (fields and functions) of inner classes are treated as public by the outer class and vice-versa.

Try this code

private float pi = 3.142;

void setup() {
  Foo f = new Foo();
  println(f.n, f.get_pi());
  exit();
}

class Foo {
  private int n = 42;

  private float get_pi() {
    return pi;
  }
}

The output is 42 3.142 proving what I say.

In Processing we can promote our inner class to a top-level class like this

public static class Foo{
   ...

Foo can no longer access pi but the attribute n is still visible but so this hasnā€™t helped. The other thing to try is putting the Foo class in its own tab called Foo.java so we have
main pde tab

void setup() {
  Foo f = new Foo();
  println(f.n);
  exit();
}

Foo.java tab

public class Foo {
  private int n = 42;
}

Now println(f.n); fails because n is a private attribute but if we make it public then it works as expected.

So what happens if we make it protected ā€¦ ?

public class Foo {
  protected int n = 42;
}

You might be surprised to know that println(f.n); still works!!!

In C++ protected limits access to the class and any child class as you might expect, this is not the full story in Java.

In Java it is called package protection so that n is accessible to the Foo class, any class that inherits from Foo and any class in the same package. Java packages are based on the storage hierarchy so any Java classes in the same folder/directory are in the same package.

Since Processing stores all the .pde and .java files in the same folder they are all in the same package so protected attributes will be treated as public.

This shows that protected access will not provide the access level you want in the Processing IDE.

Hope I have explained this clearly :grin:

1 Like

The ā€œ.javaā€ file created outta the concatenated ā€œ.pdeā€ files donā€™t have a package statement.

And a ā€œ.javaā€ file w/o a package statement belongs to the so-called ā€œdefault packageā€ as well.

If our own ā€œ.javaā€ files donā€™t have a package statement either, theyā€™ll also go to the ā€œdefault packageā€.

BtW, all my 4 Gist example links use keyword package and therefore those ā€œ.javaā€ files require the import keyword using their package namespace.

2 Likes

After reading @GoToLoop I thought

ā€˜Is it possible simply to name your own packages in Processing?ā€™

So I decided to revisit the problem and create a sketch that mimics the issue raised by the OP but with packages.I believe that the original problem boils down to

How do I make protected attributes and methods of a class unavailable to instances of the class in Processing?

I believe this sketch does just that
main sketch tab

import foopack.*;

void setup() {
  Foo f = new Foo2();  // use polymorphism
  // println(f.p);  // fail private field of top-level class
  // println(f.q);  // fail protected field of top-level class
  println(f.r);
  println("------------------------------");
  f.printValues();
  // f.printMessage("Hello world"); // fail protected method of top-level class
  exit();
}

Foo.java tab

package foopack;

public abstract class Foo {
  private int p = 31;
  protected int q = 42;
  public int r = 99;

  public abstract void printValues();
  protected abstract void printMessage(String mess);
}

Foo2.java tab

package foopack;

public class Foo2 extends Foo {

  public void printValues() {
    // System.out.println(p); // fails private member of parent class
    System.out.println(q);
    System.out.println(r);
  }

  protected void printMessage(String mess) {
    System.out.println(mess);
  }
}

Notice that protected fields and methods are no longer available by instances of the class which is the desired outcome.

2 Likes