Eu gostaria de fazer uma conversão dinâmica para uma variável Java, o tipo de conversão é armazenado em uma variável diferente.
Este é o elenco normal:
String a = (String) 5;
É isso que eu quero:
String theType = 'String';
String a = (theType) 5;
Isso é possível e, em caso afirmativo, como? Obrigado!
Atualizar
Estou tentando preencher uma classe com um HashMap
que recebi.
Este é o construtor:
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());
}
}
}
O problema aqui é que algumas das variáveis da classe são do tipo Double
, e se o número 3 for recebido, ele o verá como Integer
e eu tenho problema de tipo.
java
casting
dynamic-cast
ufk
fonte
fonte
Respostas:
Isso não faz sentido, em
String a = (theType) 5;
o tipo de
a
está estaticamente vinculado a ser,String
portanto, não faz nenhum sentido ter uma conversão dinâmica para esse tipo estático.PS: A primeira linha do seu exemplo poderia ser escrita como,
Class<String> stringClass = String.class;
mas ainda assim, você não pode usarstringClass
para converter variáveis.fonte
Sim, é possível usar o Reflection
Object something = "something"; String theType = "java.lang.String"; Class<?> theClass = Class.forName(theType); Object obj = theClass.cast(something);
mas isso não faz muito sentido, pois o objeto resultante deve ser salvo em uma variável do
Object
tipo. Se você precisa que a variável seja de uma determinada classe, você pode apenas lançar para essa classe.Se você deseja obter uma determinada classe,
Number
por exemplo: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);
mas ainda não adianta fazer isso, você pode simplesmente lançar para
Number
.Casting de um objeto NÃO muda nada; é apenas a forma como o compilador o trata.
A única razão para fazer algo assim é verificar se o objeto é uma instância da classe dada ou de qualquer subclasse dela, mas seria melhor usar
instanceof
ouClass.isInstance()
.Atualizar
de acordo com sua última atualização, o problema real é que você tem um
Integer
em seuHashMap
que deve ser atribuído a aDouble
. O que você pode fazer neste caso, é verificar o tipo do campo e usar osxxxValue()
métodos deNumber
... 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); ...
(não tenho certeza se gosto da ideia de ter o tipo errado no
Map
)fonte
Você precisará escrever mais ou menos
ObjectConverter
para isso. Isso é possível se você tiver o objeto que deseja converter e souber a classe de destino para a qual deseja converter. Neste caso particular, você pode obter a classe alvoField#getDeclaringClass()
.Você pode encontrar aqui um exemplo de tal
ObjectConverter
. Deve dar a você uma ideia básica. Se você quiser mais possibilidades de conversão, basta adicionar mais métodos com o argumento e tipo de retorno desejados.fonte
Você pode fazer isso usando o
Class.cast()
método, que converte dinamicamente o parâmetro fornecido para o tipo de instância de classe que você possui. Para obter a instância da classe de um determinado campo, você usa ogetType()
método no campo em questão. Dei um exemplo abaixo, mas observe que ele omite todo o tratamento de erros e não deve ser usado sem modificações.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); } }
fonte
Você pode escrever um castMethod simples como o mostrado abaixo.
private <T> T castObject(Class<T> clazz, Object object) { return (T) object; }
Em seu método, você deve usá-lo como
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()); } } }
fonte
Funciona e há até um padrão comum para sua abordagem: o padrão Adaptador . Mas é claro, (1) isso não funciona para converter primitivos java em objetos e (2) a classe deve ser adaptável (geralmente implementando uma interface personalizada).
Com este padrão, você poderia fazer algo como:
Wolf bigBadWolf = new Wolf(); Sheep sheep = (Sheep) bigBadWolf.getAdapter(Sheep.class);
e o método getAdapter na classe 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 }
Para você, ideia especial - isso é impossível. Você não pode usar um valor String para lançar.
fonte
Seu problema não é a falta de "casting dinâmico". Transmitir
Integer
paraDouble
não é possível. Você parece querer dar ao Java um objeto de um tipo, um campo de um tipo possivelmente incompatível, e fazer com que ele, de alguma forma, descubra automaticamente como converter entre os tipos.Esse tipo de coisa é um anátema para uma linguagem fortemente tipada como Java e IMO por boas razões.
O que realmente você está tentando fazer? Todo esse uso de reflexão parece muito suspeito.
fonte
Não faça isso. Basta ter um construtor devidamente parametrizado. O conjunto e os tipos de parâmetros de conexão são fixos de qualquer maneira, portanto, não há nenhum ponto em fazer isso tudo dinamicamente.
fonte
Pelo que vale a pena, a maioria das linguagens de script (como Perl) e linguagens de tempo de compilação não estática (como Pick) oferecem suporte a conversões de objetos dinâmicos em tempo de execução automático para (relativamente arbitrários). Isso PODE ser realizado em Java também sem perder a segurança de tipo e as boas linguagens tipadas estaticamente fornecem SEM os efeitos colaterais desagradáveis de algumas das outras linguagens que fazem coisas más com a conversão dinâmica. Um exemplo Perl que faz algumas contas questionáveis:
print ++($foo = '99'); # prints '100' print ++($foo = 'a0'); # prints 'a1'
Em Java, isso é melhor realizado (IMHO) usando um método que chamo de "cross-casting". Com a conversão cruzada, a reflexão é usada em um cache lento de construtores e métodos que são descobertos dinamicamente por meio do seguinte método estático:
Object fromString (String value, Class targetClass)
Infelizmente, nenhum método Java integrado, como Class.cast () fará isso para String para BigDecimal ou String para Integer ou qualquer outra conversão onde não há hierarquia de classes de suporte. De minha parte, o objetivo é fornecer uma maneira totalmente dinâmica de conseguir isso - para a qual não acho que a referência anterior seja a abordagem certa - ter que codificar cada conversão. Simplificando, a implementação é apenas para cast-from-string se for legal / possível.
Portanto, a solução é a simples reflexão em busca de membros públicos de:
STRING_CLASS_ARRAY = (nova classe [] {String.class});
a) Membro membro = targetClass.getMethod (method.getName (), STRING_CLASS_ARRAY); b) Membro membro = targetClass.getConstructor (STRING_CLASS_ARRAY);
Você descobrirá que todos os primitivos (Integer, Long, etc) e todos os básicos (BigInteger, BigDecimal, etc) e até mesmo java.regex.Pattern são cobertos por essa abordagem. Eu usei isso com sucesso significativo em projetos de produção onde há uma grande quantidade de entradas de valor de String arbitrárias onde alguma verificação mais estrita era necessária. Nesta abordagem, se não houver método ou quando o método é invocado, uma exceção é lançada (porque é um valor ilegal, como uma entrada não numérica para um BigDecimal ou RegEx ilegal para um Padrão), que fornece a verificação específica para a lógica inerente da classe de destino.
Existem algumas desvantagens para isso:
1) Você precisa entender bem a reflexão (isso é um pouco complicado e não para novatos). 2) Algumas das classes Java e, de fato, bibliotecas de terceiros (surpresa) não são codificadas corretamente. Ou seja, existem métodos que recebem um único argumento de string como entrada e retornam uma instância da classe de destino, mas não é o que você pensa ... Considere a classe Integer:
static Integer getInteger(String nm) Determines the integer value of the system property with the specified name.
O método acima realmente não tem nada a ver com inteiros como objetos que envolvem inteiros primitivos. O Reflection descobrirá que isso é um possível candidato para a criação incorreta de um inteiro a partir de uma string em comparação com os membros decodificadores, valueof e constructor - que são adequados para a maioria das conversões de strings arbitrárias em que você realmente não tem controle sobre seus dados de entrada, mas apenas deseja saber se é possível um Inteiro.
Para remediar o acima, procurar métodos que lançam Exceptions é um bom começo porque valores de entrada inválidos que criam instâncias de tais objetos devem lançar uma Exception. Infelizmente, as implementações variam quanto ao fato de as exceções serem declaradas como verificadas ou não. Integer.valueOf (String) lança uma NumberFormatException marcada, por exemplo, mas as exceções Pattern.compile () não são encontradas durante as pesquisas de reflexão. Novamente, não é uma falha dessa abordagem dinâmica de "cross-casting", eu acho que é uma implementação muito diferente do padrão para declarações de exceção em métodos de criação de objeto.
Se alguém quiser mais detalhes sobre como o acima foi implementado, me avise, mas eu acho que essa solução é muito mais flexível / extensível e com menos código sem perder as partes boas da segurança de tipo. Obviamente, é sempre melhor "conhecer seus dados", mas, como muitos de nós descobrimos, às vezes somos apenas destinatários de conteúdo não gerenciado e temos que fazer o melhor que podemos para usá-lo adequadamente.
Felicidades.
fonte
Então, esse é um post antigo, porém acho que posso contribuir com algo para ele.
Você sempre pode fazer algo assim:
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")); }
Claro, isso não é realmente uma conversão dinâmica, como em outras linguagens (Python, por exemplo), porque java é uma linguagem de tipo estático. No entanto, isso pode resolver alguns casos marginais em que você realmente precisa carregar alguns dados de maneiras diferentes, dependendo de algum identificador. Além disso, a parte em que você obtém um construtor com um parâmetro String provavelmente poderia ser mais flexível, tendo esse parâmetro passado da mesma fonte de dados. Ou seja, de um arquivo, você obtém a assinatura do construtor que deseja usar e a lista de valores a serem usados, dessa forma você emparelha, digamos, o primeiro parâmetro é uma String, com o primeiro objeto, convertendo-o como uma String, próximo objeto é um inteiro, etc, mas em algum momento ao longo da execução de seu programa, você obtém agora um objeto File primeiro, depois um Double, etc.
Dessa forma, você pode levar em conta esses casos e fazer um casting um tanto "dinâmico" em tempo real.
Espero que isso ajude alguém, pois isso continua aparecendo nas pesquisas do Google.
fonte
Recentemente, senti que precisava fazer isso também, mas descobri outra maneira que possivelmente torna meu código mais organizado e usa POO melhor.
Tenho muitas classes irmãs em que cada uma implementa um determinado método
doSomething()
. Para acessar esse método, eu teria que ter uma instância dessa classe primeiro, mas criei uma superclasse para todas as minhas classes irmãs e agora posso acessar o método a partir da superclasse.Abaixo eu mostro duas formas alternativas de "casting dinâmico".
// 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; }
e meu método usado atualmente,
// Method 2. mSuperFragment = (MySuperFragment) getFragmentManager().findFragmentByTag(MyHelper.getName(mUnitNum)); mSuperFragment.sortNames(sortOptionNum);
fonte
Só pensei em postar algo que achei bastante útil e que poderia ser possível para alguém que tem necessidades semelhantes.
O método a seguir foi um método que escrevi para meu aplicativo JavaFX para evitar ter que lançar e também evitar escrever se objeto x instância de instruções de objeto b toda vez que o controlador for retornado.
public <U> Optional<U> getController(Class<U> castKlazz){ try { return Optional.of(fxmlLoader.<U>getController()); }catch (Exception e){ e.printStackTrace(); } return Optional.empty(); }
A declaração do método para obter o controlador foi
public <T> T getController()
Usando o tipo U passado para o meu método por meio do objeto de classe, ele poderia ser encaminhado para o controlador get do método para dizer que tipo de objeto retornar. Um objeto opcional é retornado no caso de a classe errada ser fornecida e uma exceção ocorrer, caso em que um opcional vazio será retornado, o qual podemos verificar.
Isso é o que a chamada final para o método parecia (se presente do objeto opcional retornado leva um consumidor
fonte
Experimente isso para o Casting dinâmico. Vai funcionar!!!
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));
fonte