Search code examples
javadesign-patternspojodto

model a generic pojo to use in my classes


I'm trying to create an object that works like a generic POJO, since I have to pass it through different objects and I need an interface by which they can access its attributes.

Currently I do it with a base object that has a getField method, where I enter the name of the parameter and I get it through reflection, it's something like this:

public abstract class BasePOJO{

    public Object getField(String fieldName){

        try {
            return this.getClass().getField(fieldName).get(this);

        } catch (NoSuchFieldException ex) {
            Logger.getLogger(BaseRequest.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SecurityException ex) {
            Logger.getLogger(BaseRequest.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalArgumentException ex) {
            Logger.getLogger(BaseRequest.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            Logger.getLogger(BaseRequest.class.getName()).log(Level.SEVERE, null, ex);
        }

        return null;
    }
}

and i use this extending the class like this:

public class CredentialsPOJO extends BasePOJO{
    public String username;
    public String password;
}

and call the attributes like this:

credentialsPojo.getField("username");

I have created this quickly, it is horrible but it works. But I want to change it for something more decent to model that object.

También he creado el siguiente:

public class BasePOJO{

    private Map<String, Object> parameters = new HashMap<>();


    public void addParameter(String paramName, Object value){
        parameters.put(paramName, value);
    }

    public Object getParameter(String paramName){
        return parameters.get(paramName);
    }
}

I like this object much more, its more OO solution, the problem with this is that it only returns one Object type

The idea is that the objects that use this POJO can access their attributes in a common way. If there is a design pattern that solves this problem much better, so I can implement it.


Solution

  • Terminology clarification

    POJO = Plain Old Java Object, that is, it does not use some third-party annotations which are used to "decorate" object behaviour (Hibernate's entity annotations, or already mentioned Lombok which generates methods). What matches your case is Bean = class with getters and setters for each class field.

    Solution to your problem

    // derive type from left side of the assignement operator
    public <T> T getProperty(String paramName){
        return (T) parameters.get(paramName);
    }
    

    This way, you can type something like this:

    String name = pojo.getProperty("name");
    Integer age = pojo.getProperty("age");
    

    Drawback

    Since given method just explicitly cast map value to "any type on the left side of the assignment operator", there is no way how to achieve compile-time checking that will give you warning immediately after you write: Integer age = getProperty("name");. Note, there is also runtime type checking, when code is being executed. Such an assignement will throw an ClassCastException.

    Keep in mind, that as long as you have map defined as Map<String,Object> there is no way how to get compile-time checking - value under given key can be virtually anything. This also applies to reflection approach as getField(String) returns attribute value hidden in Object type.

    To get compile-time checking, read Recommendation section.

    Recommendation

    I would highly recommend you to use project Lombok, as already mentioned by @Peteef. Make sure you have also installed lombok plugin in your IDE, otherwise auto-generated methods would generate syntax errors.

    Annotation @Data will generate getter for each field and setter for each non-final field. In addition @Data annotation will generate also equals and hashCode methods which are important when objects are stored in Set or Map collections. Method equals might be also desirable if you want to check if two objects are the same (same fields values).

    @Data
    public class Credentials {
        public String username;
        public String password;
    }
    

    Note, you can still use simple classes with public fields without getters/setters if these classes are supposed to be used as simple data holders. Sure, it's not OOP but you don't have to blindly follow every convention (sometimes they are counter-productive).

    public class Credentials {
        public String username;
        public String password;
    }