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); }
}
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…
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.
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.
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.
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…)
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.