2

I've been struggling with it for a while now. I want to define a finite set of types, some of which would be subtypes to others, where each type is defined by values of its properties. The types would then be used to access their properties and also by a factory method to create instances of them.

It probably doesn't make much sense now so let's say I have this code.

abstract class Car {
    final int seats;
    final int maxSpeed;
    ...
}

abstract class DeliveryCar extends Car {
    final int maxload;
    ...
}

Now I want to have types such as FamilyCar and SportsCar of supertype Car and HeavyTruck of supertype DeliveryCar with multiple instances of every type and the hard part is the instance variables shouldn't really be "instance" variables - they should have the same values for every FamilyCar and then some other values for every SportsCar and still other values for every HeavyTruck and they should be accessible without an instance. So for example I want to be able to write:

System.out.print("Sports cars' max. speed: " + SportsCar.maxSpeed);

Some of the things that came to my mind but didn't work out:

  • static values, but java doesn't allow overriding static members
  • enums, but there's no subclassing of enums in java, so every Car that's not DeliveryCar would have to have the maxload and possibly other members
  • simulated enums, something like this:
class CarType {
    final int seats;
    final int maxSpeed;
    ...
    static final CarType FAMILY_CAR = new CarType(5, 180);
    static final CarType SPORT_CAR = new CarType(2, 280);
    ...
    CarType(int seats, int maxSpeed) {
    ...
}

class DeliveryCarType extends CarType {
    final int maxload;
    ...
    static final DeliveryCarType HEAVY_TRUCK = new DeliveryCarType(3, 150, 500)
    ...
    DeliveryCarType(int seats, int maxSpeed, int maxLoad) {
    ...
}

It would do what I need but then it would also be valid do wrtie something like DeliveryCar.SPORTS_CAR.

I'm not sure if there's some fundamental flaw in the design I'm trying to achieve but it's bugging me so much because I cannot seem to be able to solve something that could be solved with just overridable static members which I think some other languages (e.g. Objective-C) implement.

EDIT: I've also read this discussion (among others) but none of the ideas there seems to solve my problem.

  • Are you wanting to use polymorphism with these values? Eg: `List extends Car> listOfAnyCars ...; for(Car car : listOfAnyCars) System.out.println(car.maxSpeed);` should that be printing different values? Additionally `Car.maxSpeed` and `SportsCar.maxSpeed` should print different values? (both results not possible in this way) – xtratic Apr 12 '18 at 18:26
  • @xtratic It should be printing different values as long as the cars are of different subtypes. So all `FamilyCar`'s have the same max. speed but different than max. speed of `SportsCars`'s. I hope that makes sense. Car.maxSpeed doesn't represent anything in what I'm trying to model. It's value could be used as default for subtypes, which could override it. –  Apr 12 '18 at 18:33
  • @xtratic Access to Car.maxSpeed could as well be through a method `getMaxSpeed()` which would be abstract for `Car` if it was possible to have `abstract static` method. –  Apr 12 '18 at 18:44

2 Answers2

0

Firstly, an important note about what wont work (purely static stuff):

public static class Car {
    public final static String NAME = "Car";
    public static String getName() { return NAME; }
}

public static class SportsCar extends Car {
    public final static String NAME = "SportsCar";
    public static String getName() { return NAME; }
}

public static void main(String[] args) {
    Car c0 = new Car();
    System.out.println(c0.NAME);
    System.out.println(c0.getName());
    //  both print "Car"

    SportsCar c1 = new SportsCar();
    System.out.println(c1.NAME);
    System.out.println(c1.getName());
    // both print "SportsCar"

    Car c2 = new SportsCar();
    System.out.println(c2.NAME);
    System.out.println(c2.getName());
    // both print "Car" because c2 ""doesn't know"" that it's really a 'SportsCar'
    // this is an issue with your need for polymorphism with these values
}

If you want polymorphism then you'll need to have instance methods that return the static values, and override the instance methods to return the updated static values for their class. Maybe like this:

public static class Car {
    public final static String NAME = "Car"; 
    public final static int MAX_SPEED = 90;

    public String getName() { return NAME; }
    public int getMaxSpeed() { return MAX_SPEED; /* Car.MAX_SPEED */ }
    public String toString() { return getName() + ": maxSpeed= " + getMaxSpeed(); }
}

public static class SportsCar extends Car {
    @SuppressWarnings("hiding") public final static String NAME = "SportsCar";
    @SuppressWarnings("hiding") public final static int MAX_SPEED = 120;

    @Override public String getName() { return NAME; /* SportsCar.NAME */}
    @Override public int getMaxSpeed() { return MAX_SPEED; /* SportsCar.MAX_SPEED */ }
    // ** no new fields so just inherit 'toString()'
}

public static class DeliveryCar extends Car {
    @SuppressWarnings("hiding") public final static String NAME = "DeliveryCar";
    @SuppressWarnings("hiding") public final static int MAX_SPEED = 70;
    /* new field ........... */ public final static int MAX_LOAD = 250;

    @Override public String getName() { return NAME; /* DeliveryCar.NAME */}
    @Override public int getMaxSpeed() { return MAX_SPEED; /* DeliveryCar.MAX_SPEED */}
    /* new */ public int getMaxLoad() { return MAX_LOAD; /* DeliveryCar.MAX_LOAD */ }

    @Override public String toString() { return getName() + ": maxSpeed= " + getMaxSpeed() + ", maxLoad= " + getMaxLoad(); }
}

public static class DeliveryTruck extends DeliveryCar {
    @SuppressWarnings("hiding") public final static String NAME = "DeliveryTruck";
    @SuppressWarnings("hiding") public final static int MAX_LOAD = 1000;

    @Override public String getName() { return NAME; /* DeliveryTruck.NAME */}
    @Override public int getMaxLoad() { return MAX_LOAD; /* DeliveryTruck.MAX_LOAD */ }
    // ** no new fields so just inherit 'toString()'
}


public static void main(String[] args) {
    List<Car> list = Arrays.asList(new Car(), new Car(), new SportsCar(), new DeliveryCar(), new DeliveryTruck(), new SportsCar());
    for(Car car : list) {
        System.out.println(car);
    }
    System.out.println("Static Car maxSpeed = " + Car.MAX_SPEED);
    // warning, should access through DeliveryCar.MAX_SPEED since DeliveryTruck doesn't redefine it.
    System.out.println("Static DeliveryTruck maxSpeed = " + DeliveryTruck.MAX_SPEED);
}

I don't particularly like it, but it works... If nothing else, this is at least a bit of brainstorming to hopefully lead us to a better solution. I'd love to hear if you come up with any better ideas.

xtratic
  • 4,600
  • 2
  • 14
  • 32
  • Thanks for the effort with the code. It indeed works but it's also too easy to write `car.MAX_SPEED` instead of `car.getMaxSpeed()` and everything would seem to be working just fine but, as you showed in your first example, it would give the wrong value –  Apr 12 '18 at 20:43
  • Frankly, one should *never* call a static field or method through an instance anyway, this is generally seen as an oversight in Java. You should be able to make your IDE mark this as a warning or error. – xtratic Apr 13 '18 at 19:27
0

You don't want static fields, just use regular protected (or public) fields. You can make it final in the subclass if you want to ensure other subtypes can't override it...

abstract class  Car {
     protected int max = 5;
}

class Family extends Car {
    protected final int max = 10;
}

class Sport extends Car {
    protected final int max = 100;
}

class Other extends Car {
}

public static void main(String[] args) {
  System.out.println(new Family().max);
  System.out.println(new Sport().max);
  System.out.println(new Other().max);
}

Gives Output: 10 100 5

shifty
  • 56
  • 2
  • Yes, I know that's always possible, but I'm looking for a cleaner solution. I don't like that you need to have an instance to access value which you know beforehand and that each object has to have its own copy of every variable. I suppose both these things can affect memory if the objects are large and that's why I think there should be a better solution. –  Apr 12 '18 at 20:30