0

Is it possible to solve the following problem using inbuilt Java API? (I want to retain the strict private access as shown)

  1. I have n subclasses of an abstract class BaseModel.
  2. Each of these subclasses declare their own set of private String fields.
  3. Within the subclass constructor, I wish to set the private fields from a Map using Java Reflection. An example of this function:

    void setPrivateFields(Map<String, String> fieldsValuesMap) throws NoSuchFieldException, IllegalAccessException {
        for (Map.Entry<String, String> entry : fieldsValuesMap.entrySet()) {
            String fieldName = entry.getKey();
            String fieldValue = entry.getValue();
    
            Field field = this.getClass().getDeclaredField(fieldName);
            field.set(this, fieldValue);
        }
    }
    
  4. Is it possible to extract out the function I have described in 3) such that I do not have to rewrite the algorithm in all the constructors of the subclasses?

    class BaseModel {}
    
    class Project extends BaseModel {
        private String name;
        private String type;
    
        public Project(Map<String, String> fieldsValuesMap) {
            setPrivateFields(fieldsValuesMap);
        }
    }
    
    class Task extends BaseModel {
        private String description;
        private String rawDescription;
    
        public Task(Map<String, String> fieldsValuesMap) {
            setPrivateFields(fieldsValuesMap);
        }
    }
    
    class SubTask extends BaseModel {
      ...   
    }
    
    ...
    
abstractx1
  • 435
  • 6
  • 17
  • @jaggedSpire: Because of the similarities between the languages, I am not convinced this is a problem encountered in only Java, and it is plausible that the solution may lie less in Java, and more in logic... I may be wrong. – abstractx1 Jan 24 '17 at 16:35
  • Possible duplicate: http://stackoverflow.com/questions/16428817/convert-a-mapstring-string-to-a-pojo –  Jan 24 '17 at 16:42
  • @RC: Thank you, however, I wish to use inbuilt Java API. I've updated my question. – abstractx1 Jan 24 '17 at 16:47
  • Is there some reason you can't use `protected` over strictly `private`? – NAMS Jan 24 '17 at 16:47
  • @NAMS: Yes. These fields should be private and accessible only from the subclass from which they are declared. – abstractx1 Jan 24 '17 at 16:49
  • 1
    _"...I wish to use inbuilt Java API"_ then the C# tag is irrelevant after all. – Jorn Vernee Jan 24 '17 at 16:53
  • This approach seems way more complicated than it has to be. If the parent class has private variables, then I would include a constructor, and have all the subclass constructors include a call to `super()` and pass those values up to the parent. – NAMS Jan 24 '17 at 16:58
  • @NAMS: The parent class does not and cannot have the private variables (there are too many spread across subclasses). The parent also cannot set the fields because they a subclass private. I only want a way to extract out setting the private fields within the constructor of each subclass. – abstractx1 Jan 24 '17 at 17:04
  • Ah, I seem to have either misread or misunderstood what you were trying to ask. JornVernee's answer below is the way I would do it. – NAMS Jan 24 '17 at 17:08
  • @NAMS: No worries. Thank you for your time – abstractx1 Jan 24 '17 at 17:18

2 Answers2

2

You could simply add it to the superclass.

class BaseModel {

    protected void setPrivateFields(Map<String, String> fieldsValuesMap) {
        for (Map.Entry<String, String> entry : fieldsValuesMap.entrySet()) {
            String fieldName = entry.getKey();
            String fieldValue = entry.getValue();

            try {
                Field field = this.getClass().getDeclaredField(fieldName);
                boolean access = field.isAccessible();
                field.setAccessible(true);
                field.set(this, fieldValue);
                field.setAccessible(access);
            } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
                e.printStackTrace();
            }           
        }
    }

}
Jorn Vernee
  • 31,735
  • 4
  • 76
  • 93
  • This solves the problem I described and will accept it as is. I apologize that I did not state that I sought a solution that does not affect the accessibility of the field. This is also the only solution I found independently to you for this problem. Thank you very much for your solution and for your time. – abstractx1 Jan 24 '17 at 17:12
0

BaseModel.java

abstract class BaseModel {

}

Project.java

import java.util.Map;

class Project extends BaseModel {
    private String name;
    private String type;

    public Project(Map<String, String> fieldsValuesMap) throws NoSuchFieldException, IllegalAccessException {
        Utils.setPrivateFields(this, fieldsValuesMap);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }
}

Task.java

import java.util.Map;

class Task extends BaseModel {
    private String description;
    private String rawDescription;

    public Task(Map<String, String> fieldsValuesMap) throws NoSuchFieldException, IllegalAccessException {
        Utils.setPrivateFields(this, fieldsValuesMap);
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getRawDescription() {
        return rawDescription;
    }

    public void setRawDescription(String rawDescription) {
        this.rawDescription = rawDescription;
    }
}

Utils.java

import java.lang.reflect.Field;
import java.util.Map;

public class Utils {
    static void setPrivateFields(BaseModel baseModel, Map<String, String> fieldsValuesMap) throws NoSuchFieldException, IllegalAccessException {
        for (Map.Entry<String, String> entry : fieldsValuesMap.entrySet()) {
            String fieldName = entry.getKey();
            String fieldValue = entry.getValue();

            Field field = baseModel.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(baseModel, fieldValue);
        }
    }
}

Main.java

import java.util.HashMap;
import java.util.Map;

public class Main {

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Map<String, String> map = new HashMap<>();
        map.put("name", "ABC");
        map.put("type", "XYZ");
        Project project = new Project(map);
        System.out.println(project.getName());
        System.out.println(project.getType());
    }
}
Monzurul Shimul
  • 8,132
  • 2
  • 28
  • 42