2

I'm working on an Android app, and I'm developing on my Macbook laptop when I'm away from home and my Windows computer at home. I have the same JDK version (1.8.0_131) on both machines, and I have set the correct JDK path like shown here: How to set the JDK in Android Studio.

When I call the method Class.getDeclaredFields() on either machine, I get very different results. On my Mac, the method behaves similarly to how the Java-Doc says it should, although it seems to only see public fields. When testing on my Windows computer, the method returns ALL fields, even inherited ones, which is explicitly what the Java-Doc says the method DOES NOT do. I need Class.getDeclaredFields() to be consistent because I'm using it to build JSON files with Gson and store them on the disk elsewhere in my program.

I'm guessing that there is a different Android Runtime which is causing the program to behave differently, but I have no idea where to find that information or how to correct this problem so that it runs consistently when deployed.

On both machines I'm using a Nexus 5X virtual device with Android API 25 (Android 7.1.1 Nougat) and build tools 25.0.2.

Here's my specific implementation:

public abstract class Article {

    protected Context context;
    protected int id;//start at 0 and go up.
    private Texture texture;
    private Temperature idealTemp;
    private Formality formality;
    private String name;
    private int color;  //expressed as hex RGB
    private Bitmap image;
    JsonObject articleData;

    ...

    @Override
    public String toString() {
    //TODO: for efficiency, this should be StringBuilder
    String temp = "id: " + id + ", texture: " + texture + ", idealTemp: " + idealTemp
            + ", formality: " + formality + ", name: " + name + ", color: #" + color;

        Field[] fields = this.getClass().getDeclaredFields();
        for (Field f : fields) {
            try {
                temp = temp.concat(", " + f.getName() + ": " + f.get(this).toString());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                continue;
            }
        }

        return temp;
    }
}//end class

Where the class that inherits Article is Top which looks like this:

public abstract class Top extends Article {

    public Top(Context con, Texture tex, Temperature temp, Formality form, String n, int col, Bitmap img) {
        super(con, tex, temp, form, n, col, img);
    }

}

And finally the non-abstract class that uses the method Article.toString() is Shirt:

public class Shirt extends Top {

    //If this is not public, it is not seen by getDeclaredFields()
    //seems to be a bug in the Android Runtime on my Mac?
    public boolean longSleeves;

    public Shirt(Context con, Texture tex, Temperature temp, Formality form,
             String n, int col, Bitmap img, boolean longSleeve) {
        super(con, tex, temp, form, n, col, img);
        longSleeves = longSleeve;
    }
}

UPDATE: The code I originally posted used

getFields()

instead of

getDeclaredFields()

This has been changed and I will include images to show what both of my debuggers in Android Studio show: Mac: enter image description here

Windows: enter image description here

Matt
  • 140
  • 1
  • 3
  • 8
  • Where are you calling getDeclaredFields()? Did you include the right code in your question? – user695022 May 11 '17 at 19:13
  • I've updated my code to use getDeclaredFields(), but I have a similar problem still. See question update above. – Matt May 11 '17 at 19:42

2 Answers2

2

I'm guessing you have different code on your Mac and PC. You mention calling getDeclaredFields(), but the code you posted calls getFields(). If the code you posted is from your PC, then that is why you're seeing inherited public fields.

You're not seeing private fields because you're trying to access them without making them accessible. This throws an exception, which you log and ignore. Check your logs and you should see exceptions for each non-public field. Try adding just the names of your fields to your string or call setAccessible(true) of each field before calling get.

UPDATE:

I don't know where the change and serialVersionUID fields are coming from. I suspect there's an IDE or compiler setting to automatically include them. Since they are static fields you can filter them out. You would probably want to do this anyway.

for (Field f : getClass().getDeclaredFields()) {
    if (Modifier.isStatic(f.getModifiers()) continue;
    if (!f.isAccessible()) {
        f.setAccessible(true);
    }
    Object value = f.get(this);
    temp += ", " + f.getName() + ": " + String.valueOf(value);
}
user695022
  • 579
  • 5
  • 16
  • I would agree with you, but I don't see why something different is happening in my windows environment when they have the same code. The windows code was pulled straight from my repo after pushing from the mac. The code on my mac has not been changed and git says everything is up to date with the remote branch. – Matt May 11 '17 at 19:54
  • Thank you for all your help, this solution is pretty good. Maybe one day I'll figure out why my IDE may have compile the program this way. – Matt May 11 '17 at 23:41
  • 1
    I found [this question](https://stackoverflow.com/questions/36235608/public-static-runtime-incremental-change-android) about the incremental change field. Seems like it will only be there for debugging. – user695022 May 12 '17 at 14:27
  • oh my gosh you're right. Those extra fields are injected with instant run. Why does that feature even exist? -.- – Matt May 12 '17 at 17:04
1

I'd add this as a comment but don't have enough reputation..so
just some bits of advice:

1. Try not to use reflection, 99% of the time it is only a workaround for bad design... Think about refactoring.

2. Avoid using abstract classes at all, use interfaces instead.

Max
  • 146
  • 2
  • 9
  • I do have an implementation designed to avoid the use of reflections, but I chose this implementation because it centralizes all of the related code for multiple methods into the Article class, which means less hunting around when evolving code. I might be better off using my other design instead, you're right. – Matt May 11 '17 at 19:21