views:

869

answers:

1

Intersection types allow you to (kinda sorta) do enums that have an inheritance hierarchy. You can't inherit implementation, but you can delegate it to a helper class.

enum Foo1 implements Bar {}
enum Foo2 implements Bar {}

class HelperClass {
   static <T extends Enum<T> & Bar> void fooBar(T the enum) {}
}

This is useful when you have a number of different enums that implement some sort of pattern. For instance, a number of pairs of enums that have a parent-child relationship.

enum PrimaryColor {Red, Green, Blue;}
enum PastelColor {Pink, HotPink, Rockmelon, SkyBlue, BabyBlue;}

enum TransportMedium {Land, Sea, Air;}
enum Vehicle {Car, Truck, BigBoat, LittleBoat, JetFighter, HotAirBaloon;}

You can write generic methods that say "Ok, given an enum value thats a parent of some other enum values, what percentage of all the possible child enums of the child type have this particular parent value as their parent?", and have it all typesafe and done without casting. (eg: that "Sea" is 33% of all possible vehicles, and "Green" 20% of all possible Pastels).

The code look like this. Note in particular that the "leaf" classes themselves are quite neat - but the generic classes have declarations that are horribly ugly. That's ok: you only write them once. Once the generic classes are there, then using them is easy.

The helper class below just has some static methods. Other ways to go include

  • providing an instance that returns a singleton, but typed according to the parent/child
  • returning an instance for each paren/child, typed appropriately, and including one in each parent enum

With this second option, the "children" object would actually be inside the helper, so reducing the amount of code needed in the enumerations. They'd all instantiate a helper, and delegate anything difficult.

import java.util.EnumSet;

import javax.swing.JComponent;

public class zz extends JComponent {

    public static void main(String[] args) {
     System.out.println(PrimaryColor.Green + " " + ParentUtil.pctOf(PrimaryColor.Green) + "%");
     System.out.println(TransportMedium.Air + " " + ParentUtil.pctOf(TransportMedium.Air) + "%");
    }


}

interface Parent<P extends Enum<P> & Parent<P, C>, C extends Enum<C> & Child<P, C>> {
    Class<C> getChildClass();

    EnumSet<C> getChildren();
}

interface Child<P extends Enum<P> & Parent<P, C>, C extends Enum<C> & Child<P, C>> {
    Class<P> getParentClass();

    P getParent();
}

enum PrimaryColor implements Parent<PrimaryColor, PastelColor> {
    Red, Green, Blue;

    private EnumSet<PastelColor> children;

    public Class<PastelColor> getChildClass() {
     return PastelColor.class;
    }

    public EnumSet<PastelColor> getChildren() {
     if(children == null) children=ParentUtil.loadChildrenOf(this);
     return children;
    }
}

enum PastelColor implements Child<PrimaryColor, PastelColor> {
    Pink(PrimaryColor.Red), HotPink(PrimaryColor.Red), //
    Rockmelon(PrimaryColor.Green), //
    SkyBlue(PrimaryColor.Blue), BabyBlue(PrimaryColor.Blue);

    final PrimaryColor parent;

    private PastelColor(PrimaryColor parent) {
     this.parent = parent;
    }

    public Class<PrimaryColor> getParentClass() {
     return PrimaryColor.class;
    }

    public PrimaryColor getParent() {
     return parent;
    }
}

enum TransportMedium implements Parent<TransportMedium, Vehicle> {
    Land, Sea, Air;

    private EnumSet<Vehicle> children;

    public Class<Vehicle> getChildClass() {
     return Vehicle.class;
    }

    public EnumSet<Vehicle> getChildren() {
     if(children == null) children=ParentUtil.loadChildrenOf(this);
     return children;
    }
}

enum Vehicle implements Child<TransportMedium, Vehicle> {
    Car(TransportMedium.Land), Truck(TransportMedium.Land), //
    BigBoat(TransportMedium.Sea), LittleBoat(TransportMedium.Sea), //
    JetFighter(TransportMedium.Air), HotAirBaloon(TransportMedium.Air);

    private final TransportMedium parent;

    private Vehicle(TransportMedium parent) {
     this.parent = parent;
    }

    public Class<TransportMedium> getParentClass() {
     return TransportMedium.class;
    }

    public TransportMedium getParent() {
     return parent;
    }
}

class ParentUtil {
    private ParentUtil(){}
    static <P extends Enum<P> & Parent<P, C>, C extends Enum<C> & Child<P, C>> //
    float pctOf(P parent) {
     return (float) parent.getChildren().size() / //
       (float) EnumSet.allOf(parent.getChildClass()).size() //
       * 100f;
    }
    public static <P extends Enum<P> & Parent<P, C>, C extends Enum<C> & Child<P, C>> //
    EnumSet<C> loadChildrenOf(P p) {
     EnumSet<C> cc = EnumSet.noneOf(p.getChildClass());
     for(C c: EnumSet.allOf(p.getChildClass())) {
      if(c.getParent() == p) {
       cc.add(c);
      }
     }
     return cc;
    }
}
+1  A: 

You could just use the Commons Enum implementation instead:

http://commons.apache.org/lang/api-2.3/org/apache/commons/lang/enums/Enum.html

which allows you to create Enum's that can then be subclassed.

Jon
Real enums have benefits though, like ability to switch() on them.
StaxMan