Я хотел бы выполнить динамическое приведение для переменной Java, тип приведения хранится в другой переменной.
Это обычный кастинг:
String a = (String) 5;
Вот чего я хочу:
String theType = 'String';
String a = (theType) 5;
Возможно ли это, и если да, то как? Благодаря!
Обновить
Я пытаюсь заполнить класс HashMap
полученным мной.
Это конструктор:
public ConnectParams(HashMap<String,Object> obj) {
for (Map.Entry<String, Object> entry : obj.entrySet()) {
try {
Field f = this.getClass().getField(entry.getKey());
f.set(this, entry.getValue()); /* <= CASTING PROBLEM */
} catch (NoSuchFieldException ex) {
log.error("did not find field '" + entry.getKey() + '"');
} catch (IllegalAccessException ex) {
log.error(ex.getMessage());
}
}
}
Проблема здесь в том, что некоторые переменные класса имеют тип Double
, и если получено число 3, он видит его как, Integer
и у меня проблема с типом.
Ответы:
Это не имеет смысла в
String a = (theType) 5;
тип
a
статически привязан к нему,String
поэтому нет никакого смысла иметь динамическое приведение к этому статическому типу.PS: Первая строка вашего примера может быть записана как,
Class<String> stringClass = String.class;
но вы не можете использоватьstringClass
для приведения переменных.источник
Да, можно использовать Reflection
Object something = "something"; String theType = "java.lang.String"; Class<?> theClass = Class.forName(theType); Object obj = theClass.cast(something);
но это не имеет особого смысла, поскольку результирующий объект должен быть сохранен в переменной
Object
типа. Если вам нужна переменная определенного класса, вы можете просто привести к этому классу.Если вы хотите получить данный класс,
Number
например:Object something = new Integer(123); String theType = "java.lang.Number"; Class<? extends Number> theClass = Class.forName(theType).asSubclass(Number.class); Number obj = theClass.cast(something);
но все равно нет смысла это делать, можно просто выполнить приведение в
Number
.Приведение объекта ничего не меняет; это просто способ, которым компилятор обрабатывает это.
Единственная причина сделать что-то подобное - проверить, является ли объект экземпляром данного класса или любого его подкласса, но это лучше сделать с помощью
instanceof
илиClass.isInstance()
.Обновить
в соответствии с вашим последним обновлением реальная проблема в том, что у вас есть
Integer
файл,HashMap
который должен быть назначен наDouble
. Что вы можете сделать в этом случае, так это проверить тип поля и использоватьxxxValue()
методыNumber
... Field f = this.getClass().getField(entry.getKey()); Object value = entry.getValue(); if (Integer.class.isAssignableFrom(f.getType())) { value = Integer.valueOf(((Number) entry.getValue()).intValue()); } else if (Double.class.isAssignableFrom(f.getType())) { value = Double.valueOf(((Number) entry.getValue()).doubleValue()); } // other cases as needed (Long, Float, ...) f.set(this, value); ...
(не уверен, нравится ли мне идея неправильного типа в
Map
)источник
Вам нужно будет написать что-то вроде
ObjectConverter
этого. Это выполнимо, если у вас есть объект, который вы хотите преобразовать, и вы знаете целевой класс, в который вы хотите преобразовать. В этом конкретном случае вы можете получить целевой класс с помощьюField#getDeclaringClass()
.Вы можете найти здесь пример такого
ObjectConverter
. Это должно дать вам основную идею. Если вам нужны дополнительные возможности преобразования, просто добавьте к нему больше методов с желаемым аргументом и типом возвращаемого значения.источник
Вы можете сделать это с помощью
Class.cast()
метода, который динамически преобразует предоставленный параметр в тип имеющегося у вас экземпляра класса. Чтобы получить экземпляр класса определенного поля, вы используетеgetType()
метод для рассматриваемого поля. Я привел пример ниже, но обратите внимание, что он не включает всю обработку ошибок и не должен использоваться без изменений.public class Test { public String var1; public Integer var2; } public class Main { public static void main(String[] args) throws Exception { Map<String, Object> map = new HashMap<String, Object>(); map.put("var1", "test"); map.put("var2", 1); Test t = new Test(); for (Map.Entry<String, Object> entry : map.entrySet()) { Field f = Test.class.getField(entry.getKey()); f.set(t, f.getType().cast(entry.getValue())); } System.out.println(t.var1); System.out.println(t.var2); } }
источник
Вы можете написать простой метод castMethod, подобный приведенному ниже.
private <T> T castObject(Class<T> clazz, Object object) { return (T) object; }
В своем методе вы должны использовать его как
public ConnectParams(HashMap<String,Object> object) { for (Map.Entry<String, Object> entry : object.entrySet()) { try { Field f = this.getClass().getField(entry.getKey()); f.set(this, castObject(entry.getValue().getClass(), entry.getValue()); /* <= CASTING PROBLEM */ } catch (NoSuchFieldException ex) { log.error("did not find field '" + entry.getKey() + '"'); } catch (IllegalAccessException ex) { log.error(ex.getMessage()); } } }
источник
Это работает, и есть даже общий шаблон для вашего подхода: шаблон адаптера . Но, конечно, (1) он не работает для преобразования примитивов Java в объекты и (2) класс должен быть адаптируемым (обычно путем реализации настраиваемого интерфейса).
С помощью этого шаблона вы можете сделать что-то вроде:
Wolf bigBadWolf = new Wolf(); Sheep sheep = (Sheep) bigBadWolf.getAdapter(Sheep.class);
и метод getAdapter в классе Wolf:
public Object getAdapter(Class clazz) { if (clazz.equals(Sheep.class)) { // return a Sheep implementation return getWolfDressedAsSheep(this); } if (clazz.equals(String.class)) { // return a String return this.getName(); } return null; // not adaptable }
Для вас особенная идея - это невозможно. Вы не можете использовать значение String для приведения.
источник
Ваша проблема не в отсутствии «динамического литья». Трансляция
Integer
вDouble
вообще невозможна. Кажется, вы хотите предоставить Java объект одного типа, поле, возможно, несовместимого типа, и заставить его каким-то образом автоматически определять, как преобразовывать между типами.Подобные вещи являются анафемой для строго типизированного языка, такого как Java, и IMO по очень веским причинам.
Что вы на самом деле пытаетесь сделать? Такое использование отражения выглядит довольно подозрительно.
источник
Не делай этого. Вместо этого используйте правильно параметризованный конструктор. Набор и типы параметров подключения в любом случае фиксированы, поэтому нет смысла делать это все динамически.
источник
Как бы то ни было, большинство языков сценариев (например, Perl) и нестатических языков времени компиляции (например, Pick) поддерживают автоматические преобразования динамической строки во время выполнения в (относительно произвольные) преобразования объектов. Это МОЖЕТ быть достигнуто и в Java без потери безопасности типов, и хорошие вещи, которые статически типизированные языки обеспечивают БЕЗ неприятных побочных эффектов некоторых других языков, которые делают злые вещи с динамическим преобразованием. Пример Perl, который выполняет некоторые сомнительные вычисления:
print ++($foo = '99'); # prints '100' print ++($foo = 'a0'); # prints 'a1'
В Java это лучше сделать (IMHO) с помощью метода, который я называю «перекрестное приведение». При перекрестном преобразовании отражение используется в кэше с отложенной загрузкой конструкторов и методов, которые обнаруживаются динамически с помощью следующего статического метода:
Object fromString (String value, Class targetClass)
К сожалению, никакие встроенные методы Java, такие как Class.cast (), не сделают этого для String to BigDecimal или String to Integer или любого другого преобразования, в котором нет поддерживающей иерархии классов. С моей стороны, цель состоит в том, чтобы обеспечить полностью динамический способ достижения этого - для чего я не думаю, что предыдущая ссылка - правильный подход - необходимость кодировать каждое преобразование. Проще говоря, реализация - это просто приведение из строки, если это допустимо / возможно.
Таким образом, решение - это простое отражение, ищущее публичных членов либо:
STRING_CLASS_ARRAY = (новый класс [] {String.class});
а) Член-член = targetClass.getMethod (method.getName (), STRING_CLASS_ARRAY); б) Член-член = targetClass.getConstructor (STRING_CLASS_ARRAY);
Вы обнаружите, что все примитивы (Integer, Long и т.д.) и все основы (BigInteger, BigDecimal и т.д.) и даже java.regex.Pattern охватываются этим подходом. Я использовал это с большим успехом в производственных проектах, где существует огромное количество произвольных входных значений String, где требовалась более строгая проверка. В этом подходе, если нет метода или когда метод вызывается, генерируется исключение (потому что это недопустимое значение, такое как нечисловой ввод в BigDecimal или недопустимое регулярное выражение для шаблона), которое обеспечивает проверку, специфичную для внутренняя логика целевого класса.
У этого есть некоторые недостатки:
1) Вам нужно хорошо понимать рефлексию (это немного сложно и не для новичков). 2) Некоторые классы Java и, действительно, сторонние библиотеки (сюрприз) неправильно закодированы. То есть есть методы, которые принимают на вход единственный строковый аргумент и возвращают экземпляр целевого класса, но это не то, что вы думаете ... Рассмотрим класс Integer:
static Integer getInteger(String nm) Determines the integer value of the system property with the specified name.
Вышеупомянутый метод действительно не имеет ничего общего с целыми числами как объектами, обертывающими примитивы целыми числами. Reflection обнаружит, что это возможный кандидат на создание целого числа из String неправильно по сравнению с членами decode, valueof и constructor, которые подходят для большинства произвольных преобразований String, когда вы действительно не контролируете свои входные данные, а просто хотите знать, возможно ли это целое число.
Чтобы исправить это, поиск методов, которые генерируют исключения, является хорошим началом, поскольку недопустимые входные значения, которые создают экземпляры таких объектов, должны вызывать исключение. К сожалению, реализации различаются в зависимости от того, объявляются ли исключения как отмеченные или нет. Integer.valueOf (String), например, выдает проверенное NumberFormatException, но исключения Pattern.compile () не обнаруживаются во время поиска отражения. Опять же, это не провал этого динамического подхода «перекрестного преобразования», я думаю, что это очень нестандартная реализация для объявлений исключений в методах создания объектов.
Если кто-то захочет получить более подробную информацию о том, как было реализовано вышеупомянутое, дайте мне знать, но я думаю, что это решение намного более гибкое / расширяемое и с меньшим количеством кода без потери хороших частей безопасности типов. Конечно, всегда лучше «знать свои данные», но, как многие из нас обнаруживают, иногда мы являемся только получателями неуправляемого контента и должны делать все возможное, чтобы использовать его должным образом.
Ура.
источник
Итак, это старый пост, но я думаю, что могу кое-что внести в него.
Вы всегда можете сделать что-то вроде этого:
package com.dyna.test; import java.io.File; import java.lang.reflect.Constructor; public class DynamicClass{ @SuppressWarnings("unchecked") public Object castDynamicClass(String className, String value){ Class<?> dynamicClass; try { //We get the actual .class object associated with the specified name dynamicClass = Class.forName(className); /* We get the constructor that received only a String as a parameter, since the value to be used is a String, but we could easily change this to be "dynamic" as well, getting the Constructor signature from the same datasource we get the values from */ Constructor<?> cons = (Constructor<?>) dynamicClass.getConstructor(new Class<?>[]{String.class}); /*We generate our object, without knowing until runtime what type it will be, and we place it in an Object as any Java object extends the Object class) */ Object object = (Object) cons.newInstance(new Object[]{value}); return object; } catch (Exception e) { e.printStackTrace(); } return null; } public static void main(String[] args) { DynamicClass dynaClass = new DynamicClass(); /* We specify the type of class that should be used to represent the value "3.0", in this case a Double. Both these parameters you can get from a file, or a network stream for example. */ System.out.println(dynaClass.castDynamicClass("java.lang.Double", "3.0")); /* We specify a different value and type, and it will work as expected, printing 3.0 in the above case and the test path in the one below, as the Double.toString() and File.toString() would do. */ System.out.println(dynaClass.castDynamicClass("java.io.File", "C:\\testpath")); }
Конечно, это не совсем динамическое приведение типов, как в других языках (например, Python), потому что java - это статически типизированный язык. Однако это может решить некоторые дополнительные проблемы, когда вам действительно нужно загрузить некоторые данные по-разному, в зависимости от некоторого идентификатора. Кроме того, часть, в которой вы получаете конструктор с параметром String, вероятно, можно было бы сделать более гибким, если бы этот параметр передавался из того же источника данных. То есть из файла вы получаете подпись конструктора, которую хотите использовать, и список значений, которые будут использоваться, таким образом вы объединяете, скажем, первый параметр - это String, с первым объектом, преобразуя его как String, следующий объект - это целое число и т. д., но когда-нибудь во время выполнения вашей программы вы сначала получаете объект File, затем Double и т. д.
Таким образом, вы можете учесть эти случаи и «на лету» сделать несколько «динамическое» приведение.
Надеюсь, это поможет кому-нибудь, так как это продолжает появляться в поисках Google.
источник
Недавно я почувствовал, что должен сделать это тоже, но затем нашел другой способ, который, возможно, сделает мой код более аккуратным и использует лучшее ООП.
У меня много родственных классов, каждый из которых реализует определенный метод
doSomething()
. Чтобы получить доступ к этому методу, мне сначала нужно было бы иметь экземпляр этого класса, но я создал суперкласс для всех моих родственных классов, и теперь я могу получить доступ к методу из суперкласса.Ниже я покажу два альтернативных способа «динамического кастинга».
// Method 1. mFragment = getFragmentManager().findFragmentByTag(MyHelper.getName(mUnitNum)); switch (mUnitNum) { case 0: ((MyFragment0) mFragment).sortNames(sortOptionNum); break; case 1: ((MyFragment1) mFragment).sortNames(sortOptionNum); break; case 2: ((MyFragment2) mFragment).sortNames(sortOptionNum); break; }
и мой текущий метод,
// Method 2. mSuperFragment = (MySuperFragment) getFragmentManager().findFragmentByTag(MyHelper.getName(mUnitNum)); mSuperFragment.sortNames(sortOptionNum);
источник
Просто подумал, что опубликую что-то, что я нашел весьма полезным и могло быть возможно для тех, кто испытывает аналогичные потребности.
Следующий метод был методом, который я написал для своего приложения JavaFX, чтобы избежать необходимости выполнять приведение, а также избегать записи экземпляра if object x для операторов объекта b каждый раз, когда возвращается контроллер.
public <U> Optional<U> getController(Class<U> castKlazz){ try { return Optional.of(fxmlLoader.<U>getController()); }catch (Exception e){ e.printStackTrace(); } return Optional.empty(); }
Объявление метода для получения контроллера было
public <T> T getController()
Используя тип U, переданный в мой метод через объект класса, он может быть направлен контроллеру метода get, чтобы сообщить ему, какой тип объекта нужно вернуть. Необязательный объект возвращается, если указан неправильный класс и возникает исключение, и в этом случае будет возвращен пустой необязательный объект, который мы можем проверить.
Так выглядел окончательный вызов метода (при наличии возвращаемого необязательного объекта требуется Consumer
источник
Попробуйте это для динамической трансляции. Это сработает!!!
String something = "1234"; String theType = "java.lang.Integer"; Class<?> theClass = Class.forName(theType); Constructor<?> cons = theClass.getConstructor(String.class); Object ob = cons.newInstance(something); System.out.println(ob.equals(1234));
источник