Одиночные, Enums и анонимные внутренние классы - PullRequest
8 голосов
/ 29 сентября 2011

Как вы, возможно, знаете, некоторые люди объявляют синглтоны с Enum 1 экземпляра, потому что JVM гарантирует, что всегда будет один экземпляр без проблем с параллелизмом для обработки ...

Так что насчет Enum с несколькими экземплярами? Можем ли мы сказать что-то вроде Enum - это своего рода упорядоченный набор синглетонов, имеющих общий интерфейс? Почему?

public enum EnumPriceType {

    WITH_TAXES {
        @Override
        public float getPrice(float input) {
            return input*1.20f;
        }
        public String getFormattedPrice(float input) {
            return input*1.20f + " €";
        }
        },

    WITHOUT_TAXES {
        @Override
        public float getPrice(float input) {
            return input;
        }
    },
    ;

    public abstract float getPrice(float input);

    public static void main(String[] args) {
        WITH_TAXES.getFormattedPrice(33f);
    }

}

В этом коде почему это не работает: WITH_TAXES.getFormattedPrice (33f); Какой интерес объявить открытый метод, если он не может быть вызван без прохождения через общий интерфейс? Наверное, поэтому я не вижу синтаксиса, чтобы можно было объявить интерфейс только для одного из экземпляров Enum.


Edit:

Кажется, что экземпляры enum - это особый вид анонимных классов. Таким образом, я понимаю, почему вы не можете вызвать этот метод.

Мой вопрос в некотором роде связан с: почему анонимный класс не может реализовать интерфейс (в дополнение к интерфейсу, который он уже может реализовать!)

Я полностью понимаю, почему мы НЕ МОЖЕМ сделать это:

Vehicle veh = new Vehicle() {
    public String getName() {
        return "toto";
    }
};
veh.getName();

(getName здесь не переопределение)

Почему я не понимаю, почему мы не можем сделать это с анонимными классами:

Runnable veh = new Vehicle() implements Runnable {
    @Override
    public void run() {
        System.out.println("i run!");
    }
};
veh.run();

Или что-то, что привело бы к тому же Подумайте об этом: если вы не используете анонимные классы, вы можете абсолютно расширить класс Vehicle, а затем заставить этот подкласс реализовывать любые другие интерфейсы, которые вы хотите ...

Я почти уверен, что если бы это было возможно, мы могли бы вызывать WITH_TAXES.getFormattedPrice (33f) типобезопасным способом, поскольку WITH_TAXES не был бы реальным EnumPriceType, но это было бы лишь подклассом EnumPriceType со своим собственным интерфейса и, вызывая WITH_TAXES.getFormattedPrice (33f) с жестко запрограммированным WITH_TAXES, вы узнаете при компиляции, какой дочерний EnumPriceType вы вызываете.

Итак, мой вопрос: есть ли причины, по которым это невозможно? Или это просто еще не сделано?

Ответы [ 3 ]

11 голосов
/ 29 сентября 2011

Ваше перечисление эквивалентно следующему нормальному классу (фактически, это почти то, во что это превращает компилятор):

public abstract class EnumPriceType {

    public static final EnumPriceType WITH_TAXES = new EnumPriceType() {
        //getPrice() {...}
        //getFormattedPrice() {...}
    };

    public static final EnumPriceType WITHOUT_TAXES = new EnumPriceType() {
        //getPrice() {...}
    };

    public abstract float getPrice(float input);

    public static void main(String[] args) {
        WITH_TAXES.getFormattedPrice(33f);
    }
}

Метод getFormattedPrice() недоступен для абстрактного типа и поэтому не может быть вызван из основного метода. Подумайте, что произойдет, если метод main будет переписан с использованием локальной переменной:

public static void main(String[] args) {
    EnumPriceType foo = EnumPriceType.WITH_TAXES;
    foo.getFormattedPrice(33f);
}

Не компилируется, потому что getFormattedPrice() недоступно в базовом классе. Поскольку экземпляр WITH_TAXES является анонимным подклассом EnumPriceType, вы не можете определить локальную переменную для типа, для которого доступен метод getFormattedPrice().

В качестве мета-наблюдения это ключевое различие между языками со строгой типизацией, такими как Java, и языками типа "утка", такими как Ruby. Ruby с радостью вызовет метод getFormattedPrice(), если он там есть, независимо от того, какой тип объекта содержится в переменной foo.

Как еще одно мета-наблюдение, для разных констант одного и того же enum не имеет большого смысла иметь разные методы множеств. Если вы не можете поместить все необходимое как абстрактные (или конкретные) методы в базовый тип перечисления, вы, вероятно, используете не тот инструмент для решения проблемы.

2 голосов
/ 29 сентября 2011

Добавить

       public String getFormattedPrice(float input) {
        return input + " €";
    }

за пределами переопределения в качестве реализации по умолчанию. (Рядом с объявлением getPrice.) И вам пора.

Вы также можете использовать перечисления для реализации интерфейсов, чтобы определить, что каждый должен реализовать.

0 голосов
/ 30 сентября 2011

Так что насчет Enum с несколькими экземплярами?

Нет такой вещи, и ваш пример не демонстрирует это.У вас есть Enum с несколькими значениями . Все они являются синглетонами.

...