java: Jak mogę wykonać dynamiczne odlewanie zmiennej z jednego typu do drugiego?

Chciałbym wykonać dynamiczne odlewanie dla zmiennej java, Typ odlewania jest przechowywany w innej zmiennej.

To jest zwykły casting:

 String a = (String) 5;

This is what i want:

 String theType = 'String';
 String a = (theType) 5;
Czy to możliwe? a jeśli tak, to w jaki sposób? dzięki!

Update

Próbuję wypełnić klasę hashmapą, którą otrzymałem.

To jest konstruktor:

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

}

Problem polega na tym, że niektóre zmienne klas są typu Double, a jeśli liczba 3 jest odebrany widzi to jako liczbę całkowitą i mam problem z typem.

Author: ufk, 2010-01-24

13 answers

Jeśli chodzi o aktualizację, jedynym sposobem rozwiązania tego problemu w Javie jest napisanie kodu, który obejmuje wszystkie przypadki z dużą ilością wyrażeń if i else i instanceof. To, co próbujesz zrobić, wygląda tak, jakby było używane do programowania w językach dynamicznych. W językach statycznych to, co próbujesz zrobić, jest prawie niemożliwe i prawdopodobnie wybierzesz zupełnie inne podejście do tego, co próbujesz zrobić. Języki statyczne nie są tak elastyczne jak dynamiczne:) [12]}

Dobre przykłady Javy najlepszą praktyką jest odpowiedź autorstwa BalusC (ie ObjectConverter) oraz odpowiedź autorstwa Andreas_D (ie Adapter) poniżej.


To nie ma sensu, w

String a = (theType) 5;

Typ a jest statycznie związany z String, więc nie ma sensu mieć dynamicznego odlewu do tego typu statycznego.

PS: pierwsza linijka twojego przykładu może być zapisana jako Class<String> stringClass = String.class;, ale nadal nie możesz użyć stringClass do tworzenia zmiennych.

 12
Author: akuhn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:20

Tak możliwe jest użycie odbicia

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

Ale to nie ma większego sensu, ponieważ wynikowy obiekt musi być zapisany w zmiennej typu Object. Jeśli potrzebujesz zmiennej be z danej klasy, możesz po prostu rzucić do tej klasy.

Jeśli chcesz uzyskać daną klasę, numer na przykład:

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

Ale nadal nie ma sensu tego robić, możesz po prostu rzucić na numer.

Odlewanie obiektu niczego nie zmienia; jest to po prostu sposób kompilator traktuje to.
Jedynym powodem zrobienia czegoś takiego, jest sprawdzenie, czy obiekt jest instancją danej klasy lub jakiejkolwiek jej podklasy, ale byłoby to lepiej zrobić za pomocą instanceof lub Class.isInstance().

Update

Zgodnie z ostatnią aktualizacją prawdziwym problemem jest to, że masz liczbę całkowitą w Hashmapie, która powinna być przypisana do podwójnej. Co możesz zrobić w tym przypadku, to sprawdzić typ pola i użyć metod xxxValue() Liczba

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

(nie jestem pewien, czy podoba mi się pomysł, aby mieć zły typ na mapie)

 90
Author: Carlos Heuberger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-17 15:45:36

Będziesz musiał napisać coś w rodzaju ObjectConverter do tego. Jest to wykonalne, jeśli masz zarówno obiekt, który chcesz przekonwertować, jak i klasę docelową, na którą chcesz przekonwertować. W tym konkretnym przypadku można uzyskać klasę docelową przez Field#getDeclaringClass().

Możesz znaleźć tutaj przykład takiego ObjectConverter. To powinno dać ci pomysł na bazę. Jeśli chcesz mieć więcej możliwości konwersji, po prostu dodaj do niej więcej metod z żądanym argumentem i zwracanym typem.

 20
Author: BalusC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-24 14:29:05

Możesz to zrobić za pomocą Class.cast() metoda, która dynamicznie oddaje Podany parametr do typu posiadanej instancji klasy. Aby uzyskać instancję klasy danego pola, należy użyć getType() metoda na danym polu. Podałem przykład poniżej, ale zauważ, że pomija wszystkie obsługę błędów i nie powinien być używany bez modyfikacji.

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);
    }
}
 11
Author: Jared Russell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-24 14:57:01

To działa i istnieje nawet wspólny wzór dla Twojego podejścia: Wzór adaptera . Ale oczywiście, (1) nie działa to przy odlewaniu prymitywów Javy do obiektów i (2) klasa musi być przystosowalna (zwykle poprzez implementację niestandardowego interfejsu).

Z tym wzorem można zrobić coś takiego:

Wolf bigBadWolf = new Wolf();
Sheep sheep = (Sheep) bigBadWolf.getAdapter(Sheep.class);

I metoda getAdapter w klasie 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
}

Dla Ciebie specjalny pomysł - to jest niemożliwe. Nie możesz użyć wartości sznurka do odlewania.

 4
Author: Andreas_D,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-24 14:29:32

Możesz napisać prosty castMethod jak ten poniżej.

private <T> T castObject(Class<T> clazz, Object object) {
  return (T) object;
}

W swojej metodzie powinieneś używać jej jak

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

}
 3
Author: Hoji,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-28 07:04:25

Twoim problemem nie jest brak "dynamicznego odlewania". Przerzucanie Integer do Double nie jest w ogóle możliwe. Wydaje się, że chcesz dać Javie obiekt jednego typu, pole prawdopodobnie niezgodnego typu i sprawić, by w jakiś sposób automatycznie zorientowało się, jak konwertować między typami.

Tego typu rzeczy są przekleństwem dla mocno wpisanego języka, takiego jak Java, i IMO z bardzo dobrych powodów.

Co ty właściwie próbujesz zrobić? Całe to użycie odbicia wygląda podejrzanie.
 2
Author: Michael Borgwardt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-24 14:29:48

Nie rób tego. Wystarczy mieć odpowiednio sparametryzowany konstruktor. Ustawienia i typy parametrów połączenia są stałe, więc nie ma sensu robić tego dynamicznie.

 1
Author: Bandi-T,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-24 14:43:57

Większość języków skryptowych (takich jak Perl) i niestatycznych języków kompilacyjnych (takich jak Pick) obsługuje automatyczne dynamiczne konwersje ciągu do (względnie dowolnych) obiektów. Można to osiągnąć również w Javie bez utraty bezpieczeństwa typu, A dobre rzeczy statycznie pisane języki zapewniają bez nieprzyjemnych skutków ubocznych niektórych innych języków, które robią złe rzeczy z dynamicznym odlewaniem. Przykład Perla, który robi pewne wątpliwe Matematyka:

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

W Javie jest to lepiej osiągnięte (IMHO) za pomocą metody, którą nazywam "cross-casting". W przypadku odlewania krzyżowego, odbicie jest używane w leniwie ładowanym buforze konstruktorów i metod, które są dynamicznie odkrywane za pomocą następującej statycznej metody:

Object fromString (String value, Class targetClass)

Niestety, nie ma wbudowanych metod Javy, takich jak Class.cast () zrobi to dla String to BigDecimal lub String to Integer lub jakiejkolwiek innej konwersji, w której nie ma wspierającej hierarchii klas. Z mojej strony, Punkt jest zapewnienie w pełni dynamicznego sposobu, aby to osiągnąć - dla którego nie sądzę, że wcześniejsze odniesienie jest właściwym podejściem - konieczności kodowania każdej konwersji. Mówiąc najprościej, implementacja jest po prostu cast-from-string, jeśli jest to legalne / możliwe.

Więc rozwiązaniem jest prosta refleksja szukająca publicznych członków albo:

STRING_CLASS_ARRAY = (new Class [] {String.klasa});

A) Member member = targetClass.getMethod (method.getName (), STRING_CLASS_ARRAY); b) członek członek = targetClass.getConstructor (STRING_CLASS_ARRAY);

Znajdziesz, że wszystkie podstawowe (Integer, Long, itp.) i wszystkie podstawowe (BigInteger, BigDecimal, itp.), a nawet java.regex.W tym podejściu uwzględniono wszystkie wzorce. Użyłem tego ze znacznym sukcesem w projektach produkcyjnych, w których istnieje ogromna ilość arbitralnych wejść wartości ciągów, gdzie potrzebne było bardziej rygorystyczne sprawdzanie. W tym podejściu, jeśli nie ma metody lub gdy metoda jest wywoływana, wyjątek jest throwed (ponieważ jest to nielegalna wartość, taka jak nie-numeryczne wejście do BigDecimal lub nielegalne RegEx dla wzorca), która zapewnia sprawdzenie specyficzne dla logiki klasy docelowej.

Są pewne minusy tego:

1) musisz dobrze zrozumieć refleksję (jest to trochę skomplikowane i nie dla nowicjuszy). 2) Niektóre klasy Javy, a nawet biblioteki innych firm, nie są (niespodzianka) poprawnie zakodowane. Oznacza to, że istnieją metody, które przyjmują pojedynczy argument Łańcuchowy jako wprowadź i zwróć instancję klasy docelowej, ale nie jest to to, co myślisz... Rozważmy klasę Integer:

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

Powyższa metoda naprawdę nie ma nic wspólnego z liczbami całkowitymi jako obiektami zawijającymi pierwotne wejścia. Reflection znajdzie to jako potencjalny kandydat do tworzenia liczby całkowitej z łańcucha nieprawidłowo w porównaniu do elementów decode, valueof i konstruktor - które są odpowiednie dla większości dowolnych konwersji łańcuchów, gdzie naprawdę nie masz kontroli nad danymi wejściowymi, ale po prostu chcesz wiedzieć, czy jest możliwa liczba całkowita.

Aby zaradzić powyższej sytuacji, wyszukiwanie metod rzucających wyjątki jest dobrym początkiem, ponieważ nieprawidłowe wartości wejściowe, które tworzą instancje takich obiektów powinny rzucać wyjątek. Niestety, implementacje różnią się tym, czy wyjątki są zadeklarowane jako sprawdzone czy nie. Liczba całkowita.valueOf (String) rzuca na przykład zaznaczoną wartość NumberFormatException, ale wzorzec.wyjątki compile () nie są znajdowane podczas wyszukiwania reflection. Znowu nie brak tego dynamicznego "cross-castingu" uważam za bardzo niestandardową implementację deklaracji WYJĄTKÓW w metodach tworzenia obiektów.

Jeśli ktoś chciałby więcej szczegółów na temat tego, jak powyższe zostało zaimplementowane, daj mi znać, ale myślę, że to rozwiązanie jest znacznie bardziej elastyczne/rozszerzalne i z mniejszą ilością kodu bez utraty dobrych części bezpieczeństwa typu. Oczywiście zawsze najlepiej jest "znać swoje dane", ale jak wielu z nas uważa, czasami jesteśmy tylko odbiorcami niezarządzanych treści i musimy zrobić wszystko, co w naszej mocy, aby użyć go właściwie.

Zdrówko.
 1
Author: Darrell Teague,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-27 16:59:24

Więc, to jest stary post, jednak myślę, że mogę coś do niego wnieść.

Zawsze możesz zrobić coś takiego:

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

Oczywiście nie jest to tak naprawdę dynamiczne odlewanie, jak w innych językach (na przykład Python), ponieważ java jest statycznie wpisywanym Langiem. Może to jednak rozwiązać niektóre przypadki dodatkowe, w których rzeczywiście trzeba załadować niektóre dane na różne sposoby, w zależności od pewnego identyfikatora. Również część, w której otrzymujesz konstruktor z parametrem String, może być prawdopodobnie stał się bardziej elastyczny dzięki przekazaniu tego parametru z tego samego źródła danych. Tzn. z pliku, dostajesz podpis konstruktora, którego chcesz użyć, i listę wartości, które mają być użyte, w ten sposób sparujesz, powiedzmy, pierwszy parametr jest ciągiem znaków, z pierwszym obiektem, odlewając go jako ciąg znaków, następny obiekt jest liczbą całkowitą, itp., ale somehwere wzdłuż wykonywania programu, otrzymujesz teraz najpierw obiekt pliku, potem podwójny, itp.

W ten sposób można rozliczyć te przypadki i zrób nieco "dynamiczny" rzut w locie.

Mam nadzieję, że to pomoże komukolwiek, ponieważ to ciągle pojawia się w wyszukiwaniach Google.

 1
Author: Acapulco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-07 15:02:54

Ostatnio czułem, że też muszę to zrobić, ale potem znalazłem inny sposób, który prawdopodobnie sprawia, że mój kod wygląda schludniej i używa lepszego OOP.

Mam wiele klas rodzeństwa, z których każda implementuje określoną metodę doSomething(). Aby uzyskać dostęp do tej metody, musiałbym najpierw mieć instancję tej klasy, ale stworzyłem superclass dla wszystkich moich klas rodzeństwa i teraz mogę uzyskać dostęp do metody z superclass.

Poniżej przedstawiam dwa sposoby alternatywne do " dynamicznego casting".

// 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;
}

I moja obecnie stosowana metoda,

// Method 2.
mSuperFragment = (MySuperFragment) getFragmentManager().findFragmentByTag(MyHelper.getName(mUnitNum));
mSuperFragment.sortNames(sortOptionNum);
 0
Author: Anonsage,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-16 21:09:01

Po prostu pomyślałem, że opublikuję coś, co uznałem za bardzo przydatne i może być możliwe dla kogoś, kto doświadcza podobnych potrzeb.

Następująca metoda była metodą, którą napisałem dla mojej aplikacji JavaFX, aby uniknąć konieczności rzucania, a także unikania zapisu, jeśli instancja obiektu x instrukcji obiektu b jest zwracana za każdym razem, gdy kontroler został zwrócony.

public <U> Optional<U> getController(Class<U> castKlazz){
    try {
        return Optional.of(fxmlLoader.<U>getController());
    }catch (Exception e){
        e.printStackTrace();
    }
    return Optional.empty();
}

Deklaracją metody uzyskania kontrolera było

public <T> T getController()

Używając typu U przekazanego do mojej metody poprzez obiekt klasy, to może być przekazana do metody get kontroler, aby powiedzieć, jakiego typu obiekt ma zwrócić. Zwracany jest opcjonalny obiekt w przypadku podania niewłaściwej klasy i wystąpienia wyjątku, w którym to przypadku zostanie zwrócona pusta opcja, którą możemy sprawdzić.

Tak wyglądało ostateczne wywołanie metody (jeśli obecność zwracanego opcjonalnego obiektu zabiera konsumenta

getController(LoadController.class).ifPresent(controller->controller.onNotifyComplete());
 0
Author: Eladian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-07 01:04:23

Spróbuj tego do dynamicznego odlewania. To zadziała!!!

    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));
 0
Author: Sunil M M,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-27 02:33:39