I have a list which contains objects of different classes say A, B, C, D, E, F, G. Each of the objects(A, B, C, D, E, F, G) have an instance variable "name" in it. I want to iterate over the list and retrieve values stored in each of those object's name variable.
I tried below code
List<String> str = new ArrayList<String>();
for(Object obj: list_containing_different_objects) {
str.add(obj.name) or str.add(obj.getName());
}
But obj.name
will give error as I don't know the exact index(as it can be different each time) in the list_containing_different_objects
where A,B,C,D,E,F are stored. Also If I know the index, I couldn't fathom any solution for it.Can someone help me out of it?
CodePudding user response:
It depends on whether the classes A
, B
, C
, D
, E
, F
and G
have a common base class or interface.
If not, you could, for example, create an interface like this:
interface Nameable {
String getName();
}
and then make each of the classes implement this interface. Then you can easily just call the getName()
method on each of the objects:
for (Nameable nameable : listContainingDifferentObjects) {
str.add(nameable.getName());
}
Otherwise, if the classes will not or cannot implement a common interface or extend a base class, then the classes are unrelated, despite having a field with the same name.
In that case, you end up having to check each type, cast it and use the field1:
for (Nameable nameable : listContainingDifferentObjects) {
if (obj instanceof A) {
str.add(((A) obj).name);
}
else if (obj instanceof B) {
str.add(((B) obj).name);
}
// et cetera
}
If your classes don't implement a common type, but still have a field with the same name, I would rethink the design.
1 Since version 16, Java supports pattern matching, which simplifies the ceremony of checking type, casting and using the value. For instance:
for (Object obj : listContainingDifferentObjects) {
if (obj instanceof A a) {
str.add(a.name());
}
else if (obj instanceof B b) {
str.add(b.name());
}
// et cetera
}
See this article for more details.
CodePudding user response:
Use Reflection
Assuming the classes do not share a common base class or interface which contains the method you may use Reflection to invoke the method. There is a lot of error handling to make it work properly but the following example waves that away and forces the caller to deal with it.
public String getName(Object named)
throws NoSuchMethodException,
SecurityException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
Method method = named.getClass().getMethod("getName");
return (String) method.invoke(named);
}
The following iterates a List of objects and invokes the above method. Since the Exceptions are percolating up, this method has to handle them. You could just catch Exception here but I left the full list for completeness.
public void printNames(List<Object> namedObjects) {
for (Object obj : namedObjects) {
try {
System.out.println(getName(obj));
} catch (NoSuchMethodException |
SecurityException |
IllegalAccessException |
IllegalArgumentException |
InvocationTargetException e) {
System.err.println("Failed to invoke getName on object - skipping");
}
}
}