How to avoid a comparison by brute force?

3

Possessing any class ClassA that has a value attribute and depending on the type of value I need to perform an operation differently, as I would avoid a raw comparison, as in the following example: / p>

public class Leitor()
{

    public String lerTipoDoValor(ClassA a)
    {
         //quero verificar se é int ou Integer
         if(a.getValue() instanceOf Integer.class)
         {
             //comandos
             return "O VALOR E INTEIRO"
         }
         //quero verificar se é Double ou double
         else if(a.getValue() instanceOf Double.class )
         {
            //comandos
            return "O VALOR E DOUBLE"
         }
         else if(a.getValue() instanceOf String.class )
         {
            //comandos
            return "O VALOR E STRING"
         }
         else if(a.getValue() instanceOf Boolean.class )
         {
            //comandos
            return "O VALOR E BOOLEAN"
         }
         else if(a.getValue() instanceOf List.class )
         {
            //comandos
            return "O VALOR E LIST"
         }
         //...
    }
}

I want to consider both primitive type and its corresponding class, such as, for example, int and Integer .

    
asked by anonymous 11.02.2014 / 14:56

5 answers

3

I would not have problems with IF s for a specific system routine with a very limited scope. It would be the cleanest and most efficient way.

But if the idea is to temper your system with some of the magic that frameworks generally use so much, I would take a different approach. In fact it's not that different because it's ultimately based on a map, as some answers have already done so.

However, the fundamental difference lies in the treatment of the generic value you want to put in and take out of an HTML field. It is not enough to recover and know the type of data, it is necessary to act upon it. If we just put the type on a map and recover it, we'll still end up with% s of% s.

A worse situation is when the various developers begin to add different and custom types to the beans. Will every time a new need arise you will need to tinker with this code? Do not want this headache!

One approach would be to use the converters concept, such as JSF, JPA, and other APIs that already have this kind of feature.

Are we reinventing the wheel? Yes! But it's worth it, even for learning!

Defining a Converter

Let's define a converter that is able to place and retrieve values from HTML fields. All we need is to convert a value from a qualuqer type to String and String to the type again, ie, go round and round to an HTML field.

public interface Converter<T> {
    String toString(Object value);
    T fromString(String str);
}

Implementing basic Converters

IF

public class StringConverter implements Converter<String> {

    @Override
    public String toString(Object value) {
        return value.toString();
    }

    @Override
    public String fromString(String str) {
        return str;
    }

}

String

public class IntegerConverter implements Converter<Integer> {

    @Override
    public String toString(Object value) {
        return value.toString();
    }

    @Override
    public Integer fromString(String str) {
        return Integer.parseInt(str);
    }

}

Integer

public class DateConverter implements Converter<Date> {

    @Override
    public String toString(Object value) {
        return new SimpleDateFormat("dd/MM/yyyy").format(value);
    }

    @Override
    public Date fromString(String str) {
        try {
            return new SimpleDateFormat("dd/MM/yyyy").parse(str);
        } catch (ParseException e) {
            throw new IllegalArgumentException("Data inválida: '" + str + "'!");
        }
    }

}

Managing Converters

Now that we have some converters, let's create a class to manage all of that.

public class ConverterManager {

    private static Map<Class<?>, Converter<?>> converterMap = new HashMap<Class<?>, Converter<?>>();

    static {
        //default converters
        converterMap.put(String.class, new StringConverter());
        converterMap.put(Integer.class, new IntegerConverter());
        converterMap.put(Date.class, new DateConverter());
    }

    /**
     * Recupera um conversor de um tipo específico
     * @param classe Tipo do conversor
     * @return Instância do conversor
     */
    @SuppressWarnings("unchecked")
    public static <T> Converter<T> getConverter(Class<T> classe) {
        return (Converter<T>) converterMap.get(classe);
    }

    /**
     * Permite o registro de um novo conversor
     * @param classe Tipo do conversor
     * @param converter Instância do conversor
     */
    public static <T> void registerNewConverter(Class<T> classe, Converter<T> converter) {
        converterMap.put(classe, converter);
    }

}

The java.util.Date class initializes some standard converters and allows the developer to register new converters for the types you want.

Example usage

A simple example of how a round-trip code gets:

//um valor qualquer
Object val1 = 1;

//recupera o converter
Converter<?> converter = ConverterManager.getConverter(val1.getClass());

//converter para String
String str1 = converter.toString(val1);

//converte novamente para inteiro
Integer int1 = (Integer) converter.fromString(str1);

The difference of this basic example is that in your case you will need to use reflection to execute the getter and setter methods or directly access Field in question.

I think using attributes rather than methods is better, because the code gets more efficient and clean. However, this can cause problems if there is any logic in the methods that is important.

    
11.02.2014 / 16:00
2

You can use Map , using Class<?> as the key. For example:

public class Leitor
{
  private Map<Class<?>,String> tipoValor = new HashMap<Class<?>,String>();
  {
    tipoValor.put(Double.class, "O VALOR E DOUBLE");
    tipoValor.put(double.class, "O VALOR E DOUBLE");
    //...
    tipoValor.put(List.class, "O VALOR E LIST");
  }

  public String lerTipoDoValor(ClassA  a) {
    String res = tipoValor.get(a.getValue().getClass());
    if (res != null) {
      return res;
    }
    else {
      return "DESCONHECIDO";
    }
  }
}

If you want something more complicated, instead of using String you can put an interface, like Runnable , and put a logic specific to each type, for example:

Map<Class<?>,Runnable> tipoValor = new HashMap<Class<?>,Runnable>();
tipoValor.put(Double.class, new Runnable(){
  public void run() {
    //Faz alguma coisa se for double
  }
});
tipoValor.put(Integer.class, new Runnable(){
  public void run() {
    //Faz alguma coisa se for int
  }
});

then:

Runnable r = tipoValor.get(a.getValue().getClass());
if (r != null) { 
    r.run();
}

If you need to receive parameters or return a value, you can create your own interface.

    
11.02.2014 / 15:09
1

You could create a HashMap containing the types and their description. So, if I understood your intention correctly, it would be enough to search for HashMap and capture its corresponding description. It would look like this:

HashMap<Object, String> mapa = new HashMap<Object, String>();

Roughly:

mapa.put(Integer.class, "Inteiro");
mapa.put(String.class, "String");
mapa.put(Decimal.class, "Decimal");
//e assim por diante

And what would your method look like:

public String lerTipoDoValor(ClassA a)
{
    System.out.println("O VALOR E " + mapa.get(a.class));
}

I think this way the code becomes more readable and organized.

UPDATE

After reviewing your comments, I've identified that your actual intent is to run a specific code based on the type of object, not just return it type. So, here's my opinion:

Create an interface:

public interface ITipo
{
    void ExecutaMetodo();
}

Implement classes, related to types:

public class BehaviorInteger implements ITipo
{
    @Override
    public void ExecutarMetodo()
    {
        System.out.println("Este tipo é inteiro");
    }
}

public class BehaviorString implements ITipo
{
    @Override
    public void ExecutarMetodo()
    {
        System.out.println("Este tipo é String");
    }
}

Now comes the HashMap question:

HashMap<Object, ITipo> mapa = new HashMap<Object, ITipo>();

Populating HashMap:

mapa.put(Integer.class, new BehaviorInteger());
mapa.put(String.class, new BehavioString());
//e assim por diante

And what would your method look like:

public String lerTipoDoValor(ClassA a)
{
    mapa.get(a.class).ExecutarMetodo();
}

The code may contain some syntax errors, since I do not have any Java IDE at the moment, but this is not my primary working language.

I think this is the best way to implement it because of code organization and ease of maintenance.

Ah. Do not forget to check that the type is contained in the HashMap before executing the method.

    
11.02.2014 / 15:09
0

I think the most correct way is to class A implement a interface .

Each class will implement interface according to the type of data it contains.

The code that uses these classes will not need to know anything about the class, you just need to call the interface method and the class will execute the appropriate commands.

    
11.02.2014 / 15:18
0

I think the best option is to use Overloading in your class. So you implement the same method several times, each one accepting a parameter type. For example:

public class Leitor()
{
    public String lerTipoDoValor(ClassA a)
    {
         return this.interpretaA(a.getValue());
    }

    public String interpretaA(Integer value)
    {
        return "O VALOR E INTEIRO";
    }

    public String interpretaA(Double value)
    {
        return "O VALOR E DOUBLE";
    }

    public String interpretaA(String value)
    {
        return "O VALOR E STRING";
    }

    public String interpretaA(Boolean value)
    {
        return "O VALOR E BOOLEAN";
    }

    public String interpretaA(List value)
    {
        return "O VALOR E LIST";
    }
}
    
11.02.2014 / 15:25