Расширение enums через наследование
Я знаю, что это скорее противоречит идее перечислений, но возможно ли продлить перечисление на C # / Java? Я имею в виду «расширение» как в смысле добавления новых значений в перечисление, так и в смысле OO наследования от существующего enums.
Я предполагаю, что это невозможно в Java, так как он получил их довольно недавно (Java 5?). C # кажется более прощающим людей, которые хотят делать сумасшедшие вещи, поэтому я подумал, что это возможно в некотором роде. Предположительно, его можно было взломать через reflection (не то, чтобы вы на самом деле использовали этот метод)?
Я не обязательно заинтересован в реализации какого-либо данного метода, это просто вызвало мое любопытство, когда это пришло мне в голову 🙂
- HttpServletRequest для полного URL-адреса
- Как работает Class.forName ()?
- Оптимизация утечки памяти в JavaFX
- Работа со списком списков в Java
- Eager / автозагрузка EJB / нагрузки EJB при запуске (на JBoss)
- Разделение строки на каждый n-й символ
- Как добавить локальные файлы jar в проект Maven?
- Как сделать AWT Button () и использовать ImageIcon (), Icon ()?
- Проблема с wav в Java
- Замена кода с помощью обработчика annotations
- Перечисления: Почему? Когда?
- Пользовательский десериализатор JSON с использованием Gson
- Как избежать исключения ArrayIndexOutOfBoundsException или IndexOutOfBoundsException?
Причина, по которой вы не можете расширять Enums, состоит в том, что это приведет к проблемам с polymorphismом.
Скажем, у вас есть enum MyEnum со значениями A, B и C и расширьте его значением D как MyExtEnum.
Предположим, что метод ожидает значение myEnum где-то, например, в качестве параметра. Должно быть законным предоставлять значение MyExtEnum, потому что это подтип, но теперь, что вы собираетесь делать, когда получится значение D?
Чтобы устранить эту проблему, расширение перечислений является незаконным
Когда встроенных перечислений недостаточно, вы можете сделать это по-старому и сделать свой собственный. Например, если вы хотите добавить дополнительное свойство, например, поле описания, вы можете сделать это следующим образом:
public class Action { public string Name {get; private set;} public string Description {get; private set;} private Action(string name, string description) { Name = name; Description = description; } public static Action DoIt = new Action("Do it", "This does things"); public static Action StopIt = new Action("Stop It", "This stops things"); }
Затем вы можете рассматривать его как переписку так:
public void ProcessAction(Action a) { Console.WriteLine("Performing action: " + a.Name) if (a == Action.DoIt) { // ... and so on } }
Хитрость заключается в том, чтобы убедиться, что конструктор является закрытым (или защищенным, если вы хотите наследовать), и что ваши экземпляры являются статическими.
Вы ошибетесь: в подclassе enums будет меньше записей.
В псевдокоде подумайте:
enum Animal { Mosquito, Dog, Cat }; enum Mammal : Animal { Dog, Cat }; // (not valid C#)
Любой метод, который может принять Животное, должен иметь возможность принимать Млекопитающее, но не наоборот. Подclass предназначен для создания чего-то более конкретного, не более общего. Вот почему «объект» является корнем иерархии classов. Аналогично, если enums наследуются, то гипотетический корень иерархии перечислений будет иметь все возможные символы.
Но нет, C # / Java не разрешают sub-enums, AFAICT, хотя иногда это было бы очень полезно. Вероятно, потому, что они решили реализовать Enums как ints (например, C) вместо интернированных символов (например, Lisp). (Выше, что означает (Animal) 1, и что представляет (Mammal) 1, и являются ли они одинаковым значением?)
Однако вы могли бы написать свой собственный enum-подобный class (с другим именем). С атрибутами C # это может показаться приятным.
Предполагается, что enums представляют собой перечисление всех возможных значений, поэтому распространение скорее противоречит идее.
Однако, что вы можете сделать в Java (и предположительно C ++ 0x), есть интерфейс вместо classа enum. Затем поставьте стандартные значения в перечисление, которое реализует эту функцию. Очевидно, вы не можете использовать java.util.EnumSet и тому подобное. Это подход, примененный в «больше возможностей NIO», который должен быть в JDK7.
public interface Result { String name(); String toString(); } public enum StandardResults implements Result { TRUE, FALSE } public enum WTFResults implements Result { FILE_NOT_FOUND }
Вы можете использовать отображение .NET для извлечения ярлыков и значений из существующего enums во время выполнения ( Enum.GetNames()
и Enum.GetValues()
– это два конкретных метода, которые вы использовали бы), а затем использовать Enum.GetValues()
кода для создания нового один с этими элементами плюс некоторые новые. Это кажется несколько аналогичным «наследованию от существующего enums».
Добавление перечислений – довольно обычная вещь, если вы вернетесь к исходному коду и отредактируете, любым другим способом (наследование или reflection, если это возможно), скорее всего, вернется и ударит вас, когда вы получите обновление библиотеки и они ввели одно и то же имя enums или одно и то же значение enums – я видел много кода низкого уровня, где целое число соответствует двоичной кодировке, где вы столкнулись с проблемами
В идеале ссылки на ссылки на ссылки должны быть записаны только как равные (или переключатели) и пытаться быть будущими доказательствами, не ожидая, что значение enums будет const
Я не видел, чтобы кто-то еще упоминал об этом, но значение параметра enums важно. Например, с граалями при сохранении enums в базе данных он использует порядковое значение. Если бы вы могли каким-то образом расширить перечисление, какими будут порядковые значения ваших расширений? Если вы расширили его в нескольких местах, как бы вы могли сохранить какой-то порядок для этих ординалов? Хаос / нестабильность в порядковых значениях будет плохой, что, вероятно, является еще одной причиной, по которой разработчики языка не коснулись этого.
Еще одна сложность, если вы были разработчиком языка, как вы можете сохранить функциональность метода values (), который должен возвращать все значения enums. Что бы вы вызвали это и как бы он собрал все ценности?
Если вы имеете в виду расширение в смысле базового classа, то в Java … нет.
Но вы можете расширить значение enums, чтобы иметь свойства и методы, если это то, что вы имеете в виду.
Например, следующее перечисляет перечисление Кранке:
class Person { enum Bracket { Low(0, 12000), Middle(12000, 60000), Upper(60000, 100000); private final int low; private final int high; Brackets(int low, int high) { this.low = low; this.high = high; } public int getLow() { return low; } public int getHigh() { return high; } public boolean isWithin(int value) { return value >= low && value <= high; } public String toString() { return "Bracket " + low + " to " + high; } } private Bracket bracket; private String name; public Person(String name, Bracket bracket) { this.bracket = bracket; this.name = name; } public String toString() { return name + " in " + bracket; } }
Увидел сообщение об этом для Java некоторое время назад, посмотрите http://www.javaspecialists.eu/archive/Issue161.html .
Вы не можете наследовать / расширять перечисление, вы можете использовать атрибуты для объявления описания . Если вы ищете целочисленное значение, оно встроено.
Хммм – насколько я знаю, этого не может быть сделано – enums записываются во время разработки и используются для удобства программиста.
Я уверен, что когда код будет скомпилирован, эквивалентные значения будут заменены именами в вашем перечислении, тем самым устраняя концепцию enums и, следовательно, возможность ее расширения.
Я хотел бы иметь возможность добавлять значения к enumsм C #, которые являются комбинациями существующих значений. Например (это то, что я хочу сделать):
AnchorStyles определяется как
public enum AnchorStyles { None = 0, Top = 1, Bottom = 2, Left = 4, Right = 8, }
и я хотел бы добавить AnchorStyles.BottomRight = Right + Bottom, чтобы вместо того, чтобы говорить
my_ctrl.Anchor = AnchorStyles.Right | AnchorStyles.Bottom;
Я могу просто сказать
my_ctrl.Anchor = AnchorStyles.BottomRight;
Это не вызывает никаких проблем, о которых говорилось выше, поэтому было бы неплохо, если бы это было возможно.
Что касается java, это не допускается, потому что добавление элементов в enum будет эффективно создавать суперclass, а не подclass.
Рассматривать:
enum Person (JOHN SAM} enum Student extends First {HARVEY ROSS}
В общем случае polymorphism будет
Person person = Student.ROSS; //not legal
что явно неверно.
Некоторое время назад даже я хотел сделать что-то подобное и обнаружил, что расширения enum будут скрывать множество базовых понятий … (не просто полиморфный)
Но все равно вам может понадобиться сделать, если перечисление объявлено во внешней библиотеке и помните, что вам следует проявлять особую осторожность при использовании этих расширений enum …
public enum MyEnum { A = 1, B = 2, C = 4 } public const MyEnum D = (MyEnum)(8); public const MyEnum E = (MyEnum)(16); func1{ MyEnum EnumValue = D; switch (EnumValue){ case D: break; case E: break; case MyEnum.A: break; case MyEnum.B: break; } }