Совет по кодированию — типы пересечений и перечисления Java

Типы пересечения позволяют вам (своего рода) делать перечисления, которые имеют иерархию наследования. Вы не можете наследовать реализацию, но можете делегировать ее вспомогательному классу.

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

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

Это полезно, когда у вас есть несколько различных перечислений, которые реализуют какой-то шаблон. Например, ряд пар перечислений, которые имеют отношение родитель-потомок.

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;}

Вы можете написать универсальные методы, которые говорят: «Хорошо, учитывая значение перечисления, которое является родителем некоторых других значений перечисления, какой процент всех возможных дочерних перечислений дочернего типа имеют это конкретное родительское значение в качестве родителя?», и иметь все это. typesafe и выполняется без приведения типов. (например: что «Море» составляет 33% всех возможных транспортных средств, а «Зеленый» — 20% всех возможных пастельных тонов).

Код выглядит так. В частности, обратите внимание, что "листовые" классы сами по себе довольно изящны, но общие классы имеют ужасно уродливые объявления. Это нормально: вы пишете их только один раз. Когда общие классы есть, их легко использовать.

Вспомогательный класс ниже имеет только несколько статических методов. Другие способы включают

  • предоставление экземпляра, который возвращает синглтон, но типизированный в соответствии с родительским/дочерним элементом
  • возвращение экземпляра для каждого родительского/дочернего элемента, набранного соответствующим образом и включающего один экземпляр в каждое родительское перечисление

С этим вторым вариантом «дочерний» объект фактически будет внутри помощника, что уменьшит объем кода, необходимого для перечислений. Все они создавали экземпляр помощника и делегировали что-либо сложное.

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;
    }
}

person paulmurray    schedule 22.02.2009    source источник
comment
На самом деле это не вопрос, но, возможно, его стоит перефразировать в вопрос, на который вы сами отвечаете, чтобы мы могли проголосовать за него.   -  person Andrew Coleson    schedule 22.02.2009
comment
Это не вопрос, но я думаю, что на SO есть место для чего-то подобного. Я знаю, что Джеффу эта идея не нравится, так что ты можешь рассердиться.   -  person Iain Holder    schedule 22.02.2009
comment
Я должен согласиться с предыдущими комментариями, было бы намного лучше, если бы это было записано в виде вопроса (помните, что вы можете отвечать на свои вопросы).   -  person David Z    schedule 22.02.2009
comment
Я не согласен с тем, что SO - это место для такого рода вещей. Это действительно запись в блоге. Если его можно записать в виде вопроса, то напишите вопрос - если это хорошее решение, за него проголосуют, если есть лучшее решение, то нет. -1 Боюсь, и я бы проголосовал за закрытие, если бы мог.   -  person Nick Fortescue    schedule 22.02.2009
comment
@Nick Fortescue Джефф сказал, что он хотел создать SO как место для людей, которые не склонны вести блог, чтобы делиться своими знаниями.   -  person Iain Holder    schedule 22.02.2009
comment
@IainMH - я знаю, но для ответов на вопросы. Джефф по-прежнему пишет сообщения в блогах. Этот довольно близок к границе, но я думаю, что если автор не может переписать как вопрос, то его следует закрыть. Если это то, для чего предназначен SO, его можно перефразировать как вопрос   -  person Nick Fortescue    schedule 23.02.2009
comment
Вопрос - в чем вопрос?   -  person amit    schedule 25.02.2009
comment
Ну, я не блокирую, потому что я просто недостаточно крут, чтобы каждый день говорить что-то интересное. Или неделя. Поэтому я сделал это как пост.   -  person paulmurray    schedule 28.02.2009
comment
Было бы хорошо, если бы это было сформулировано как ответ на вопрос.   -  person Paŭlo Ebermann    schedule 28.07.2011


Ответы (2)


Вместо этого вы можете просто использовать реализацию Commons Enum:

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

который позволяет вам создавать Enum, которые затем могут быть подклассами.

person Jon    schedule 01.07.2009
comment
Однако у реальных перечислений есть преимущества, такие как возможность переключать() на них. - person StaxMan; 11.07.2009
comment
реальные перечисления также обрабатываются специально сериализацией java. - person paulmurray; 16.02.2011

это проще, он делает то, что вы хотите?

import java.util.*;
interface Tree{
    Tree parent();
    Set<Tree> children();
}
enum PrimaryColor implements Tree {
    Red,Green,Blue;
    @Override public Tree parent() {
        return null;
    }
    @Override public Set<Tree> children() {
        return Collections.unmodifiableSet(children);
    }
    final Set<Tree> children=new LinkedHashSet<Tree>();
}
enum PastelColor implements Tree {
    Pink(PrimaryColor.Red),HotPink(PrimaryColor.Red),Rockmelon(PrimaryColor.Green),SkyBlue(PrimaryColor.Blue),BabyBlue(PrimaryColor.Blue);
    PastelColor(final PrimaryColor primaryColor) {
        this.primaryColor=primaryColor;
        if (primaryColor!=null) primaryColor.children.add(this);
    }
    @Override public Tree parent() {
        return primaryColor;
    }
    @Override public Set<Tree> children() {
        return Collections.emptySet();
    }
    double percent() {
        return primaryColor.children().size()*100./EnumSet.allOf(super.getClass()).size();
    }
    private final PrimaryColor primaryColor;
}
public class Main{
    static double percent(final Tree tree) {
        final Tree parent=tree.parent();
        if (tree instanceof Enum) { return parent.children().size()*100./((Enum)tree).getClass().getEnumConstants().length; }
        else throw new RuntimeException("strange tree!");
    }
    public static void main(String[] args) {
        System.out.println("one way");
        for(PastelColor pastelColor:PastelColor.values())
            System.out.println(pastelColor+" "+pastelColor.percent());
        System.out.println("another way");
        for(PastelColor pastelColor:PastelColor.values())
            System.out.println(pastelColor+" "+percent(pastelColor));
    }
}
person Ray Tayek    schedule 12.03.2011
comment
Дело не в том, чтобы приводить объекты к Enum, а в том, чтобы они были типобезопасными во время компиляции. - person paulmurray; 22.06.2011