Подтвердить что ты не робот

Java: как я могу выполнять динамическое кастинг переменной из одного типа в другой?

Я хотел бы сделать динамическое кастинг для переменной 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());         
        }
    }

}

проблема заключается в том, что некоторые из переменных классов являются двойными, и если число 3 получено, оно видит его как Integer и у меня проблема типа.

4b9b3361

Ответ 1

Что касается вашего обновления, единственный способ решить эту проблему на Java - написать код, который охватывает все случаи с большим количеством выражений if и else и instanceof. То, что вы пытаетесь сделать, выглядит так, как если бы оно использовалось для программирования с динамическими языками. На статических языках то, что вы пытаетесь сделать, почти невозможно, и, вероятно, вы бы выбрали совершенно другой подход к тому, что вы пытаетесь сделать. Статические языки не так гибки, как динамические:)

Хорошими примерами лучшей практики Java являются ответ BalusC (т.е. ObjectConverter) и ответ Andreas_D (т.е. Adapter) ниже.


Это не имеет смысла, в

String a = (theType) 5;

тип a статически связан с String, поэтому нет никакого смысла, чтобы динамический приведение к этому статическому типу.

PS: Первая строка вашего примера может быть записана как Class<String> stringClass = String.class;, но все же вы не можете использовать stringClass для преобразования переменных.

Ответ 2

Да, можно использовать Reflection

Object something = "something";
String theType = "java.lang.String";
Class<?> theClass = Class.forName(theType);
Object obj = theClass.cast(something);

но это не имеет большого смысла, поскольку результирующий объект должен быть сохранен в переменной типа Object. Если вам нужна переменная be данного класса, вы можете просто применить ее к этому классу.

Если вы хотите получить заданный класс, например 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().

Update

в соответствии с последним обновлением, настоящая проблема заключается в том, что у вас есть Integer в вашей HashMap, который должен быть назначен Double. Что вы можете сделать в этом случае, это проверить тип поля и использовать методы xxxValue() для номера

...
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);
...

(не уверен, что мне нравится идея неправильного типа на карте)

Ответ 3

Для этого вам нужно написать ObjectConverter. Это выполнимо, если у вас есть и объект, который вы хотите преобразовать, и вы знаете целевой класс, к которому вы хотите преобразовать. В этом конкретном случае вы можете получить целевой класс Field#getDeclaringClass().

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

Ответ 4

Вы можете сделать это, используя метод 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);
    }
}

Ответ 5

Это работает и существует даже общий шаблон для вашего подхода: Шаблон адаптера. Но, конечно, (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 для кастинга.

Ответ 6

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

Подобные вещи являются анафемой строго типизированного языка, такого как Java, и IMO по очень веским причинам.

Что вы на самом деле пытаетесь сделать? Все, что использует отражение, выглядит довольно подозрительно.

Ответ 7

Вы можете написать простой метод 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());          
    }    
}

}

Ответ 8

Не делай этого. Вместо этого просто создайте правильно параметризованный конструктор. Набор и типы параметров соединения в любом случае фиксированы, поэтому нет никакого смысла делать это все динамически.

Ответ 9

Для того, что стоит, большинство языков сценариев (например, Perl) и нестатических языков времени компиляции (например, Pick) поддерживают автоматическую динамическую последовательность String для (относительно произвольных) объектов. Это МОЖЕТ быть реализовано на Java, а также без потери безопасности типов и хороших материалов, которые статически типизированные языки обеспечивают БЕЗ неприятных побочных эффектов некоторых других языков, которые делают зло с динамическим кастингом. Пример Perl, который вызывает некоторую сомнительную математику:

print ++($foo = '99');  # prints '100'
print ++($foo = 'a0');  # prints 'a1'

В Java это лучше выполняется (IMHO), используя метод, который я называю "кросс-кастинг". С кросс-литьем отражение используется в ленивом кэше конструкторов и методов, которые динамически обнаруживаются с помощью следующего статического метода:

Object fromString (String value, Class targetClass)

К сожалению, никакие встроенные Java-методы, такие как Class.cast(), не будут делать это для String для BigDecimal или String для Integer или любого другого преобразования, где нет поддерживающей иерархии классов. Со своей стороны, цель состоит в том, чтобы обеспечить полностью динамичный способ достижения этой цели - для которой я не думаю, что предыдущая ссылка - правильный подход - необходимость кодировать каждое преобразование. Проще говоря, реализация является просто отличным от строки, если это законно/возможно.

Таким образом, решение является простым отражением, ищущим публичных членов:

STRING_CLASS_ARRAY = (новый класс [] {String.class});

a) Член-член = targetClass.getMethod(method.getName(), STRING_CLASS_ARRAY); b) Член-член = targetClass.getConstructor(STRING_CLASS_ARRAY);

Вы обнаружите, что все примитивы (Integer, Long и т.д.) и все основы (BigInteger, BigDecimal и т.д.) и даже java.regex.Pattern охвачены этим подходом. Я использовал это со значительным успехом в производственных проектах, где имеется огромное количество произвольных значений значений String, где требуется более строгая проверка. В этом подходе, если нет метода или когда метод вызывается, генерируется исключение (поскольку это недопустимое значение, такое как нечисловой ввод в BigDecimal или незаконный RegEx для шаблона), который обеспечивает проверку, специфичную для логика целевого класса.

Есть некоторые недостатки:

1) Вам нужно хорошо понимать рефлексию (это немного сложно, а не для новичков). 2) Некоторые из классов Java и действительно сторонних библиотек (сюрпризов) неправильно кодируются. То есть существуют методы, которые принимают один строковый аргумент в качестве входных данных и возвращают экземпляр целевого класса, но это не то, что вы думаете... Рассмотрим класс Integer:

static Integer getInteger(String nm)
      Determines the integer value of the system property with the specified name.

Вышеуказанный метод действительно не имеет ничего общего с целыми в качестве объектов, объединяющих примитивы ints. Reflection найдет это как возможный кандидат для создания Integer from String некорректно по сравнению с членами decode, valueof и constructor, которые подходят для большинства произвольных преобразований String, где вы действительно не контролируете свои входные данные, но просто хотите, чтобы знать, возможно ли целое число.

Чтобы исправить это, поиск методов, которые вызывают исключения, является хорошим началом, поскольку недопустимые входные значения, которые создают экземпляры таких объектов, должны вызывать исключение. К сожалению, реализации различаются в зависимости от того, объявлены ли исключения как проверенные или нет. Integer.valueOf(String) выдает исключенное исключение NumberFormatException, но исключения Pattern.compile() не найдены во время поиска отражений. Опять же, не провал этого динамического подхода "кросс-кастинг", я думаю, что это очень нестандартная реализация для объявлений исключений в методах создания объектов.

Если кто-то хочет получить более подробную информацию о том, как это было реализовано, дайте мне знать, но я думаю, что это решение гораздо более гибкое/расширяемое и с меньшим количеством кода, не теряя при этом хороших частей безопасности типов. Конечно, всегда лучше "знать ваши данные", но, как многие из нас находят, мы иногда являемся только получателями неуправляемого контента и должны делать все возможное, чтобы использовать его должным образом.

Приветствия.

Ответ 10

Итак, это старое сообщение, но я думаю, что могу что-то внести в него.

Вы всегда можете сделать что-то вроде этого:

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, может быть, вероятно, более гибкой, поскольку этот параметр передается из одного источника данных. То есть из файла вы получаете подпись конструктора, которую вы хотите использовать, и список значений, которые будут использоваться, таким образом, вы, например, первый параграф, являетесь строкой, с первым объектом, выставляя его как строку, следующий объект - это Integer и т.д., но некоторые из них выполняются при выполнении вашей программы, вы сначала получаете объект File, затем Double и т.д.

Таким образом, вы можете учитывать эти случаи и выполнять "динамическое" литье "на лету".

Надеюсь, это поможет любому, поскольку это продолжает появляться в поиске Google.

Ответ 11

Недавно мне показалось, что я тоже должен был это сделать, но затем нашел другой способ, который, возможно, делает мой код более аккуратным и использует лучший OOP.

У меня есть много классов сестер, каждый из которых реализует определенный метод 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);

Ответ 12

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

Следующий метод - это метод, который я написал для моего приложения JavaFX, чтобы избежать необходимости приведения в действие, а также избегать записи, если экземпляр объекта 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 get метода, чтобы сообщить ему, какой тип объекта должен возвращаться. Необязательный объект возвращается в случае, если указан неправильный класс и возникает исключение, и в этом случае возвращается пустой необязательный элемент, который мы можем проверить.

Это то, как выглядел последний вызов метода (если в нем присутствует возвращаемый необязательный объект, объект Consumer

getController(LoadController.class).ifPresent(controller->controller.onNotifyComplete());

Ответ 13

Попробуйте это для Dynamic Casting. Это будет работать!!!

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