How to avoid a comparison by brute force?

Asked

Viewed 411 times

3

Possessing a class of any kind ClassA which has an attribute value and that, depending on the type of value i need to perform an operation differently, as I would avoid a gross comparison, as in the following example:

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, as, for example, int and Integer.

  • 1

    Do you have any specific example or context where you are using it? Class architecture might not be the best for this situation. This comparison does not look ideal (as referred to)

  • 1

    You can’t just solve it with a.getValue().getClass().getName()? Then just create a map and associate the class name to a return.

  • Well, the goal of my project is to turn a bean into an HTML form using annotations.

  • Not because where I commented //commands would be different commands for each comparison.

  • What Classea represents in its project?

  • to create the html form, the main classes are Beaninfo(which amazerna a bean, and values in annotations),Fieldinfo(Amazernar a field and values in annotations), Beanconverter(Convert a bean into html tags ),Fieldconverter(converts the field into html tags), Tag(represents an html tag), Attribute(represents an html attribute),the problem is in Fieldconverter, its main function and create html inputs, and for each type of Attribute a different input is generated ex : for a bool or Boolean and generated 2 Radiobuttons, for a Date and generated input of type datetime-local and etç..

Show 1 more comment

5 answers

4


I wouldn’t have a problem with the IFs for a system-specific routine with a very limited scope. It would be the cleanest and most efficient.

But if the idea is to spice up your system with a little bit of the magic frameworks in general use so much, I would take a different approach. Actually it’s not so much different, because in the end it’s based on a map, as some answers have already done.

However, the fundamental difference is 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 guy on a map and recover, we’ll end up with several IFs.

An even worse situation is when the various developers start adding different and customized types to the Beans. Will every time a new need arises you need to fiddle with that code? Don’t want that headache!

One approach would be to use the concept of converters (converters), like JSF, JPA and other Apis that already have this type of resource.

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

Defining a Convert

Let’s define a converter that is able to put and recover HTML field values. All we need to do is convert a value from a qualuqer type to String and from String to type again, that is, to go back and forth to an HTML field.

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

Implementing Converters basic

String

public class StringConverter implements Converter<String> {

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

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

}

Integer

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

}

java.util.Date

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 the Converters

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

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 class ConverterManager initializes some standard converters and allows the developer to register new converters for the types you want.

Example of use

A simple example of what a round-trip code looks like:

//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 methods getter and Setter or directly access the Field in question.

I believe that using attributes instead of methods is better, because the code is more efficient and clean. However, this can cause problems if there is any logic in the methods that is important.

  • 1

    face, vlw genius problem solving, I was wondering just how these frameworks treated the same problem, a while ago, for didactic purpose, I tried to make a kind of JPA for C#, but I ended up being overshadowed by the same problem

  • @user5020 Good to have helped.

2

You can use a Map, using as Class<?> as 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 specific logic for 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
  }
});

and 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.

  • Runnable’s very good guy, knows when you keep complaining because you haven’t had this idea before , vlw by help

1

You could create a HashMap containing the types and their respective description. So, if I understood your purpose correctly, just search the 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 your method would 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 identified that your real purpose is to execute a specific code according to the type of obejto, and not just to return your type. So follow 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 the Hashmap:

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

And what your method would look like:

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

The code may contain some syntax errors, as I do not currently have any Java IDE other than this being my main working language.

I believe this is the best form of implementation due to code organization and ease of maintenance.

Ah. Don’t forget to check that the type is contained in the Hashmap before running the method.

  • in Hashmap it is possible to add a function, or method in the value?

  • @user5020, I edited my answer with the solution to your question.

  • a.class does not compile. If your key Map is a class, you shouldn’t use Object. There is no class called Decimal. I tried to edit your answer twice, but the edits were rejected.

  • I’m sorry @Victor, but I just started in the O.R., and I’m still getting used to it. I saw that you edited correctly, but I could not find the option to accept the changes. This is why I informed in my reply that Java is not my main working language and that I do not have any Java IDE installed at the moment. So I thank you for your contribution and your time spent.

0

I think the right way is class A implement a interface.

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

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

  • I even thought about it, however, I would have to create a Factory that would use the same gross comparison to manufacture the object

  • 1

    As I do not know the context in which you use these classes and where you have to instantiate them, I cannot add anything more to my answer. Anyway I think that the factorywill be a better solution.

0

I believe the best option is to use Overloading in your class. So you implement the same method several times, each accepting a type of parameter. 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";
    }
}
  • The parameter passed by argument would always be of the Classa type, causing its overloads would never be invoked.

  • @Joaoraposo a is an instance of ClasseA (therefore the method lerTipoDoValor), however the amount received by a.getValue() can be an instance of any class.

  • It is quite right, without the context of the problem would say that and' a solution to avoid all if’s chained. + 1

  • 1

    The function overload in your response is resolved at compile time. At that time, the type of a.getValue() is (probably) Object, and in practice only a function would be called always. It is necessary to check the type of the object at runtime.

  • @C.E.Gesser now I was in doubt, since I am not a Java programmer. Another possible solution would be to map to/from associating strings of possible classes with a callback.

  • 1

    That’s what I proposed in my reply :)

  • @C.E.Gesser It was a response worthy of Java programmer :)

  • 1

    Being a . net programmer the answer makes perfect sense. I can’t comment for java. @rodrigorigotti misses so' a "Return" in the first method.

  • Actually I program very little lately. 99% of my work is with C++ nowadays.

  • in all cases would always call the interpreter(Object obj), but thanks

Show 5 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.