0

Is it possible to list, inside a class, all its static fields automatically, i.e., without explicitly adding them to a list myself?

For example: I have a base class Vehicle that contains a number of class fields that are derived classes (Car, Truck, Van). How can I get the field VEHICLES populated without having to add each Car, Truck, etc. manually? Is this possible?

public class Vehicle {
    public static final Car a = new Car(...);
    public static final Car b = new Car(...);
    public static final Truck c = new Truck(...);
    ...
    public static final Van d = new Van(...);
    
    public static FINAL List<Vehicle> VEHICLES;
    static {
        VEHICLES = new ArrayList<>();
        // Add all static fields here   
    }

And, more specifically, would I be able to list per derived class, like so

    public static FINAL List<Car> CARS; // populate with all static fields of class Car
    public static FINAL List<Truck> TRUCKS; // populate with all static fields of class Truck
    ...

I have done some searching, and it seems that reflection might be the way to go (for example, this and this question are in the right direction) - but I cannot figure out how to 'translate' the Fields into objects and add them (if possible at all):

    public static Field[] fields; 
    static {
        fields = Vehicle.class.getDeclaredFields();
        for (Field f : fields) {
            VEHICLES.add(...); // to add any Vehicle
            if (f.getType().isInstance(Car.class)) {
                CARS.add(...); // to add all Cars
            }
        }
    }

Am I way off, and should it be done differently altogether? Or is this not possible, or maybe even a code smell?

rdv
  • 682
  • 2
  • 8
  • 19
  • Sorry, but the whole approach is definitely code smell, and it's not clear what you are trying to achieve. Please add information describing your overall goals here. – Jim Garrison Apr 05 '22 at 18:22
  • Are you trying to generate source code? – ernest_k Apr 05 '22 at 18:30
  • @JimGarrison The class Vehicle would contain all instances of Vehicle subclasses that I need, and in other parts of my code I would like to iterate over a list all these - like I would do with .values(), if Vehicle were an enum. – rdv Apr 05 '22 at 18:31
  • Still not clear... Are all the _"instances of Vehicle subclasses "_ singletons? Are you trying to emulate an `enum`? If so, why can't you use an `enum`? – Jim Garrison Apr 05 '22 at 18:39
  • @JimGarrison No, they're not singletons. The example is a simplification of my real code. Basically, I want to have a more or less fixed set of Vehicle subclass objects that are instantiated (as static fields) in the Vehicle superclass. I then want to use these instances elsewhere, but often I won't know the instance itself but only the value of one of its fields (let's say 'number plate'), which is different for each Vehicle. This I wanted to do by iterating over a list, or by using a Map for looking up - but I don't know how to make these, other than manually. I hope this makes sense. – rdv Apr 05 '22 at 19:16
  • @JimGarrison I can't use enums because the objects have functionality that I can't (or at least, don't know how to) achieve using enums. – rdv Apr 05 '22 at 19:17
  • Why do you need to have named variables? Why can't you just create all the instances in the constructor (or a static initialization block) and store them in a collection without having actual instance variables? I fear this is an [XY Problem](http://xyproblem.info) and if you shared your ultimate goals there might be a much better approach. – Jim Garrison Apr 05 '22 at 19:21
  • @JimGarrison That won't work, as elsewhere in the code I need to be able to access the instances both by their name (e.g., in case I know the Car but need one of its properties) and by their properties (e.g., in case I only know a unique property, like number plate, but need the Car that goes with it). So I guess my ultimate goal, put as simply as possible, is to have a list of all these instances; a lookup-tabe, if you will. I can make this list manually, of course, but I wanted to know if that is avoidable. I am sorry if all this doesn't make sense, I not a professional programmer! – rdv Apr 05 '22 at 19:33

1 Answers1

0

I would say that's it's definitely a code smell since reflection should be carefully used in regular projects (by regular I mean everything but frameworks like Spring).

But if you want to use it, here is how you can do it

class Vehicle {
  public static final Car a = new Car();
  public static final Car b = new Car();
  public static final Truck c = new Truck();

  public static final List<Vehicle> VEHICLES;

  static {
    VEHICLES = new ArrayList<>();
    Field[] declaredFields = Vehicle.class.getDeclaredFields();
    for (Field field : declaredFields) {
      if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
        if (Vehicle.class.isAssignableFrom(field.getType())) {
          try {
            VEHICLES.add((Vehicle) field.get(null));
          } catch (IllegalAccessException e) {
            e.printStackTrace();
          }
        }
      }
    }
  }
}
geobreze
  • 2,274
  • 1
  • 10
  • 15
  • Thanks for this! If reflection is not actually meant for my problem, then I will be happy to try another solution. – rdv Apr 05 '22 at 19:34
  • Could you please clarify how it doesn't solve your problem? Class `Vehicle` has 3 static variables `a`, `b` and `c` which are subclasses of `Vehicle` class. After this class is being loaded variable `VEHICLES` contains `a`, `b` and `c`. What am I missing here? – geobreze Apr 05 '22 at 19:39
  • It does solve my problem, but since you also remark that "it's definitely a code smell since reflection should be carefully used in regular projects", I am now curious if there is a better/cleaner solution to list these fields. I'll leave this open for a bit; if nothing comes I will accept your answer. – rdv Apr 05 '22 at 22:51
  • 1
    I don't see any other solution to the problem you've described. But why do you need those static variables? Why don't just create a list and initialize it with your intances without declaring static variable for every instance? – geobreze Apr 06 '22 at 08:14
  • 1
    Don’t use an exception handler like `catch(IllegalAccessException e) { e.printStackTrace(); }`. The exception should never happen. But if it does, for whatever reason, the program should not continue. Use something like `catch(IllegalAccessException e) { throw new ExceptionInInitializerError(e); }` – Holger Apr 06 '22 at 08:56
  • Accepted this as the answer because it answers what I asked - but I ended up refactoring my code and not using it because it's too convoluted. Thanks for the insights :) – rdv Apr 08 '22 at 12:03