Looping different class instances


#1

I’ve kind of painted myself into a corner. I need to be able to loop through instances of different classes to run methods of a class they all instantiate.

I know something like this is a good candidate for an Interface, but it’s too late for that – the class is a common utility with many methods used by many other classes, and cannot be easily converted to an Interface.

This is a very stripped down version of what I’m trying to do. Is there any way I can loop through a list of a, b, c to call each one’s h.printHello()?

void setup(){
  A a = new A();
  B b = new B();
  C c = new C();
  
  a.h.printHello();
  b.h.printHello();
  c.h.printHello();
}


class A {
  Helper h = new Helper("A");
}

class B {
  Helper h = new Helper("B");
}

class C {
  Helper h = new Helper("C");
}

class Helper {
  String hello = "hello ";
  Helper (String s){ hello+=s; }
  void printHello() { println(hello); }
}

#2

Actually, I think I figured out a workaround… it’s not elegant, but it works:

void setup(){
  ArrayList<A> abc = new ArrayList<A>();
  abc.add(new A()); abc.get(0).h = new Helper("A");
  abc.add(new A()); abc.get(1).h = new Helper("B");
  abc.add(new A()); abc.get(2).h = new Helper("C");
  
  for (A a : abc){
    a.h.printHello();
  }
}

PS Does this forum have a preview mode before I submit a post? Oh… I see it right to my right as I type this here now… Must remember to be more observant…

PPS Sorry for the gratuitous post… Still, if anyone has a solution to the first question I’d be interested, though it strikes me as against the vary nature of java to be able to do something like that…


#3
// https://discourse.processing.org/t/looping-different-class-instances/5843/2
// GoToLoop (2018/Nov/23)

final ABC[] abc = { new A(), new B(), new C() };

void setup() {
  for (final ABC spell : abc)  spell.h.printHello();
  exit();
}

class Helper {
  String hello = "Hello ";

  Helper() {
  }

  Helper(final String s) { 
    hello += s;
  }

  void printHello() { 
    println(hello);
  }

  String toString() {
    return hello;
  }
}

abstract class ABC {
  final Helper h = new Helper();

  String toString() {
    return h.toString();
  }
}

class A extends ABC {
  A() {
    h.hello += 'A';
  }
}

class B extends ABC {
  B() {
    h.hello += 'B';
  }
}

class C extends ABC {
  C() {
    h.hello += 'C';
  }
}

#4

Here a different approach using reflection that searches for all the objects, sees if they contain an instance of Helper, and calls printHello if they do.

import java.lang.reflect.Field;

A a;
B b;
C c;

void setup() {
  a = new A();
  b = new B();
  c = new C();

  printHelloOnAll(this);
}

void printHelloOnAll(Object container) {
  // traverse all fields in container object
  for (Field field : container.getClass().getDeclaredFields()) {
    try {
      // get the instance associated to this field
      Object obj = field.get(container);
      // for that instance, find fields of class "Helper"
      // and call printHello() on them
      for (Field objField : obj.getClass().getDeclaredFields()) {
        if (objField.getType() == Helper.class) {
          ((Helper) objField.get(obj)).printHello();
        }
      }
    } 
    catch (Exception e) {
      e.printStackTrace();
    }
  }
}

class A { Helper h = new Helper("A"); }
class B { Helper h = new Helper("B"); }
class C { Helper h = new Helper("C"); }

class Helper {
  String hello = "hello ";
  Helper (String s) { hello+=s; }
  void printHello() { println(hello); }
}

This works even if some of the A B C … classes do not have a Helper instance.


#5

Reflection is over-the-top this can easily be done in object orientation using inheritance and polymorphism.

void setup() {
  Base[] names = { new A("Abel"), new B("Byron"), new C("Cain") };
  for (Base b : names) {
    b.printHello();
  }
}

class Base {
  Helper h;

  Base(String n) {
    h = new Helper(n);
  }

  void printHello() {
    h.printHello();
  }
}

class A extends Base {
  A(String n) {
    super(n);
  }
}

class B extends Base {
  B(String n) {
    super(n);
  }
}

class C extends Base {
  C(String n) {
    super(n);
  }
}

class Helper {
  String hello = "hello ";
  String name ="";

  Helper (String n) {
    name = n;
  }
  void printHello() { 
    println(hello + name);
  }
}

I should say that OO has its own semantics so that although this works it may not be the best approach but without knowing what the classes A, B, C and Helper represent I can’t offer anything much better.


#6

It is over the top, but can be added and removed without touching the rest of your code :slight_smile:

ps. And I’m not defending the use of reflection. It would be much better to use extends or implements properly.


#7

What a terrific lesson I’ve just received from you all… Thank you all very much! I will be studying each solution until I’ve internalized them.

I feel like you’ve all just helped me level-up in java. :star_struck:

Update 2hours later: It’s working now: labour and time saved without having to jump through painful hoops. Most grateful!


#8

Abstract classes and reflection are fine solutions given your constraint (no interface!) but I’m a bit confused about why this is an issue.

If you don’t want to map a large method list up from Helper to A / B / C, then don’t. You can still use an interface.

Just create an interface Helpful with only one method – Helper getHelper(), which returns the Helper object of that A/B/C object. Your classes A B C each implement that one method which makes them Helpful (class A implements Helpful … class B implements Helpful …) – and then in your main code you loop through a list of Helpful objects, call Helper h = obj.getHelper() on each of them, and then access h.printHello() or whatever method(s) you need on that Helper that was returned. No need to touch your underlying hairy utility class at all – just give your heterogeneous collection of classes the simplest possible interface to return that object.

Code or it didn’t happen:

void setup() {
  Helpful[] helpers = { new A(), new B(), new C() };
  for (Helpful obj : helpers) {
    // single method quick access
    obj.getHelper().printHello();
    // or retrieve the helper for multiple method calls
    Helper h = obj.getHelper();
    h.printHello();
  }
}

interface Helpful {
  Helper getHelper();
}

class A implements Helpful {
  Helper h = new Helper("A");
  Helper getHelper() { 
    return h;
  }
}

class B implements Helpful {
  Helper h = new Helper("B");
  Helper getHelper() { 
    return h;
  }
}

class C implements Helpful {
  Helper h = new Helper("C");
  Helper getHelper() { 
    return h;
  }
}

class Helper {
  String hello = "hello ";
  Helper (String s) { 
    hello+=s;
  }
  void printHello() { 
    println(hello);
  }
}

Note that, depending on the details of what you are trying to do, this Breaks Encapsulation. However if you are trying to access an arbitrary list of methods on this object without adding them to your wrapper classes then it sounds like you are already fine with this.


Collision between two ArrayList elements
#9

That seems like an elegant solution, thank you for it!

That said, what are the practical ramifications of subclassing a baseclass or using such an interface? Does one provide advantages over another? Or, in this case, are they just two different approaches that offer a comparable solution? (I have no doubt there are deep & technical differences, but a CS technical analysis will leave me staring into oncoming headlights…)


#10

Java allows a subclass to extends 1 parent class only. :coffee:
But a class (or even an interface) can implements as many interfaces as we want. :star_struck:

  1. Processing.org/reference/extends.html
  2. Processing.org/reference/implements.html

Interface and classes
#11

For practical purposes, one difference is that you can only extend one class, but you can implement many interfaces. This makes them more fluid and plastic than inheritance.

Here is an example:

class Pasta {
  Flour f = new Flour();
  Eggs e = new Eggs();
}
class Omelette {
  Eggs e = new Eggs();
  Milk m = new Milk();
}
class Pancake {
  Milk m = new Milk();
  Flour f = new Flour();
}

These classes form a simple Venn diagram, with each including and excluding certain things that you might want to expose in a mixed list. If you created abstract classes for Floury, Milky, and Eggy, they would not help you, as Java does not support multiple inheritance – you can only extend one class. But as interfaces, you can make some of your classes Floury and Milky, and some of your classes Milky and Eggy, and multiple “implements” works just fine.