0

Say I have a Car abstract class:

public abstract class Car {

    public enum Feature {
        FAST, SEXY, RELIABLE, FUEL_EFFICIENT
    };

    public static List<Feature> getFeatures() {
        return new ArrayList<Car.Feature>();
    }

    public abstract int getMaxSpeed();
}

and a number of Car implementations with various constructors like these two:

public class CarA extends Car {

    private static List<Feature> features = Arrays.asList(new Feature[] {
            Feature.FAST, Feature.SEXY });
    private int maxSpeed;

    public CarA(int year) {
        maxSpeed = year > 2000 ? 240 : 220;
    }

    public static List<Feature> getFeatures() {
        return features;
    }

    @Override
    public int getMaxSpeed() {
        return maxSpeed;
    }

}

and

public class CarB extends Car {

    private static List<Feature> features = Arrays
            .asList(new Feature[] { Feature.FAST });
    private int maxSpeed;

    public CarB(int year, int modelNumber) {
        if (modelNumber > 10) {
            maxSpeed = year > 2010 ? 180 : 160;
        } else {
            maxSpeed = 100;
        }
    }

    public static List<Feature> getFeatures() {
        return features;
    }

    @Override
    public int getMaxSpeed() {
        return maxSpeed;
    }

}

I want to write a car class factory that suggests a number of car instances with various configurations depending on the taste of a person:

public class CarFactory {

    public static class UserPreferences {
        public List<Feature> requiredFeatures;
        public int minimumSpeed;
    }

    public List<Car> getMatchingCars(int year, UserPreferences preferences) {
        List<Class<?>> candidateCarClasses = Arrays.asList(new Class<?>[] {
                CarA.class, CarB.class });
        List<Class<?>> carClassesWithIncorrectFeatures = new ArrayList<Class<?>>();
        for (Class<?> candidateClass : candidateCarClasses) {
            Class<Car> carClass = (Class<Car>) candidateClass;
            // XXX how do I check carClass.getFeatures() are included in
            // requiredFeatures? carClass.getMethod("getFeatures",null)???
        }
        candidateCarClasses.removeAll(carClassesWithIncorrectFeatures);

        List<Car> cars = new ArrayList<Car>;
        for (Class<?> candidateClass : candidateCarClasses) {
            if (CarA.class.equals(candidateClass.getClass())) {
                CarA carA = new CarA(year);
                            // the rest is easy...
            } else if (...) {
                            // the rest is easy...     
        }
        }
        return cars;
    }
}

My question is:

how do I access the static methods of the various car classes without having to instantiate any car object at location XXX in my code?

And if that can't be done or if I am not approaching this properly, what would be the right way to solve my problem? I.e. how can my car factory trim out first all the classes that do not have the correct features before having to instantiate the candidate classes that are left. (In the real situation I am contemplating, I have many object instances to consider for each class and the constructors are resource intensive.)

Obviously, I could move the features supported by each car implementation outside of the classes into a separate map, but it seems a cleaner approach to have all info related to a specific car instance in that implementation rather that split across multiple locations.

Lolo
  • 3,935
  • 5
  • 40
  • 50
  • 2
    See http://stackoverflow.com/questions/22525727/static-members-and-interfaces/22525856#22525856 - not quite a duplicate (it's C#, for starters), but related. Basically, you can't do this - static methods aren't polymorphic. – Jon Skeet Mar 20 '14 at 15:39
  • I guess you should really have a `List` instead of `List>` in your `getMatchingCar()` method. – Rohit Jain Mar 20 '14 at 15:40
  • 1
    Incidental observation, use a `Set` rather than a `List`; as you have an `enum` you can use [`EnumSet`](http://docs.oracle.com/javase/7/docs/api/java/util/EnumSet.html) to get very concise code: `Set features = EnumSet.of(Feature.FAST, Feature.SEXY);` – Boris the Spider Mar 20 '14 at 15:43
  • @JonSkeet Jon, you are everywhere! I have read so many of your useful answers over the years. The question you pointed me to is indeed similar and it is unfortunate there isn't a good answer for it. It sounds like I may have to take the list of car features outside of each car implementation then, forcing me to have car-related info in a couple of places. Not ideal but if that's the only option... – Lolo Mar 20 '14 at 15:44
  • This code doesn't seem at all polymorphic anyway - what are you gaining by not just taking the features in a constructor? Polymorphism is to do with _behaviour_, you just have data. – Boris the Spider Mar 20 '14 at 15:44
  • @RohitJain I don't think so. I first want to go through the classes and trim out the ones not worth further exploration, and then return the instances. – Lolo Mar 20 '14 at 15:45
  • @BoristheSpider Thanks for the tip Boris. – Lolo Mar 20 '14 at 15:46
  • @BoristheSpider This abstract class Car is just an example. In my actual code, the abstract class does have a lot of methods describing behavior that some subclasses override. But I don't understand your suggestion about taking the features in the constructor: can you be more concrete? Note that each subclass will support a fixed set of features so I do not see why that should be passed in. – Lolo Mar 20 '14 at 16:01

0 Answers0