简体   繁体   中英

Java: Iterate through Nested Classes

Currently, I have a number of nested classes inside an outer class. Each nested class has an overriden toString method. My goal is to iterate through these nested classes to access the value returned by the overriden toString without initializing every single nested class. I've tried iterating through the array returned by Class#getDeclaredClasses

The simplified outer class:

class MyClass {
    static class NestedClassOne {
        @Override
        public String toString() {return "One";}
    }
    static class NestedClassTwo {
        @Override
        public String toString() {return "Two";}
    }
}

The method I've tried, using the Class#getDeclaredClasses array:

        for (Class<?> NestedClass : MyClass.class.getDeclaredClasses()) {
            System.out.println(NestedClass.toString());
        }

This method, to my dismay, only prints the following:

class ... MyClass$NestedClassTwo
class ... MyClass$NestedClassOne

Your toString() is invoked directly on the class object. Ie, it calls Class<T>.toString() not NestedClassOne.toString() . For that you need to use reflection:

  Class<NestedClassOne> clazz = NestedClassOne.class;
  Method method = clazz.getMethod( "toString" );
  NestedClassOne obj = clazz.newInstance();

  Object result = method.invoke( obj );
  System.out.println( result );

I'll repeat my comments however:

This sounds like a bad idea. You really shouldn't be using reflection like this. An enum or something similar would likely be a better fit.

It's just kind of obviously super brittle. It throws numerous exceptions, which enums won't. If the code structure changes at all (changing to a different constructor for example) it breaks. If the method name changes, the compiler can't detect it. Etc. I wouldn't want to maintain code that works the way you are asking for.

My goal is to 1) iterate through these nested classes to 2) access the value returned by the overridden toString without initializing every single nested class .

The first part you are doing. The second part is not possible.

The toString() methods you are trying to call are instance methods.

  • You can't call an instance method until you have created an instance.
  • You can't create an instance of a class without triggering 1 the static initialization of the class.

What your attempted code is actually doing is calling the toString() method on the Class objects. That gives you the names of the classes.

@markspace's answer shows how to create an instance and call the toString() method via reflection. The caveats are that it assumes that each class has a no-args constructor, and that creating the instances doesn't have undesirable side-effects.


1 - There are scenarios involving classes with cyclic dependencies where an instance of a class may be created before its static initialization has completed.


I agree with @markspace's comments. I'm not sure what the real purpose of this is, but there is likely to be a better (more robust) way than calling toString() methods. (For example... using a custom annotation with runtime retention.)

Even if there isn't, the constraint of not initializing the classes seems artificial to me: is this a premature optimization?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM