Я ищу умные способы создать динамические классы Java, который является классами, где можно добавить/удалить поля во времени выполнения. Сценарий использования: у Меня есть редактор, где пользователи должны смочь добавить поля к модели во времени выполнения или возможно даже создать целую модель во времени выполнения.
Некоторые цели дизайна:
HashMap
? Возможно, используйте массив и присвойте индексы полям во время установки?)Обратите внимание, что это, "думают вне кругового" вопроса. Я отправлю пример ниже для получения Вас в настроении :-)
Типобезопасный без приведения типов, если это возможно, для пользовательского кода, который работает с динамическими полями (этот код может быть получен из плагинов, которые расширяют модель непредвиденными способами)
AFAIK, это невозможно. Вы можете получить безопасность типов без приведения типов только в том случае, если вы используете статическую типизацию. Статическая типизация означает сигнатуры методов (в классах или интерфейсах), которые известны во время компиляции.
Лучшее, что вы можете сделать, - это иметь интерфейс с набором методов, например String getStringValue (String field)
, int getIntValue (String field)
и так далее. И, конечно, вы можете сделать это только для заранее определенного набора типов. Любое поле, тип которого не входит в этот набор, потребует преобразования типа.
Итак, в основном вы пытаетесь создать новый вид объектной модели с более динамическими свойствами, немного похожий на динамический язык?
Возможно, стоит взглянуть на исходный код Rhino (т.е. Javascript, реализованный на Java), который сталкивается с аналогичной проблемой реализации системы динамических типов на Java.
Из всего вышесказанного, я подозреваю, что вы обнаружите, что внутренние HashMaps в конечном итоге работают лучше для ваших целей.
Я написал небольшую игру (Tyrant - доступен источник GPL), используя подобную динамическую объектную модель с использованием HashMaps, она отлично работала, и производительность не была проблемой. Я использовал несколько трюков в методах get и set, чтобы позволить динамические модификаторы свойств, я уверен, что вы можете сделать то же самое для реализации ваших сигналов и отношений родитель/ребенок и т.д.
[EDIT] Смотрите в источнике BaseObject, как это реализовано.
Очевидный ответ - использовать HashMap
(или LinkedHashMap
, если вам нужен порядок поля). Затем вы можете добавить динамические поля с помощью метода get (String name)
и set (String name, Object value)
.
Этот код может быть реализован в общем базовом классе. Поскольку существует всего несколько методов, также просто использовать делегирование, если вам нужно расширить что-то еще.
Чтобы избежать проблемы с приведением типов, вы можете использовать типобезопасную карту объектов :
TypedMap map = new TypedMap();
String expected = "Hallo";
map.set( KEY1, expected );
String value = map.get( KEY1 ); // Look Ma, no cast!
assertEquals( expected, value );
List<String> list = new ArrayList<String> ();
map.set( KEY2, list );
List<String> valueList = map.get( KEY2 ); // Even with generics
assertEquals( list, valueList );
Уловка здесь - это ключ, который содержит информацию о типе:
TypedMapKey<String> KEY1 = new TypedMapKey<String>( "key1" );
TypedMapKey<List<String>> KEY2 = new TypedMapKey<List<String>>( "key2" );
Производительность будет в порядке.
Повторное использование поля - это использование того же типа значения или расширение ключевого класса типобезопасной карты объектов с помощью дополнительных функций.
Вычисляемые поля могут быть реализованы с помощью второй карты, в которой хранятся экземпляры Future
, которые выполняют вычисления.
Поскольку все манипуляции производятся всего двумя (или, по крайней мере, несколькими) методами, отправка сигналов проста и может выполняться любым удобным для вас способом.
Чтобы реализовать автоматическую обработку родительских / дочерних сигналов, установите прослушиватель сигналов для сигнала «установить родительский» дочернего элемента, а затем добавьте дочерний элемент к новому родительскому элементу (и при необходимости удалите его из старого).
Поскольку фреймворк не используется и никаких уловок не требуется, результирующий код должен быть довольно чистым и легким для понимания. Отсутствие использования String в качестве ключей имеет дополнительное преимущество: люди не будут засорять код строковыми литералами.