0

I'm using enums for units of different physical quantities, like meter and miles for the DISTANCE. To use them in a generic way there is an interface Unit which has the method convert(double).

To load the preferred units, a singleton is used:

public class UnitPreferences {

    private static UnitPreferences sInstance;

    private HashMap<PhysicalQuantity, Unit> mUnits;

    /**
     * Returns the instance of the preferred units collection.
     *
     * @param context the context
     * @return Instance of the preferred units collection.
     */
    public static UnitPreferences from(Context context) {
        if (sInstance == null) {
            sInstance = new UnitPreferences(context);
        }
        return sInstance;
    }

    /**
     * Creates a new set of preferred units by fetching them from the Shared Preferences.
     *
     * @param context the resources
     */
    private UnitPreferences(Context context) {
        // Load the preferred units from SharedPreferences and put them in the mUnits HashMap
    }

    /**
     * Returns all units of a specific physical quantity.
     *
     * @param physicalQuantity the physical quantity
     * @return All units available to this physical quantity.
     */
    private Unit[] getAllUnits(PhysicalQuantity physicalQuantity) {
        switch (physicalQuantity) {
            case DISTANCE:
                return DistanceUnit.values();

            // others...

            default:
                throw new RuntimeException("No units defined for " + physicalQuantity);
        }
    }

    /**
     * Returns the preferred unit of a physical quantity.
     *
     * @param phQuantity the physical quantity
     * @return The preferred unit.
     */
    public Unit getPreferredUnit(PhysicalQuantity phQuantity) {
        return mUnits.get(phQuantity);
    }
}

The PhysicalQuantity enum:

public enum PhysicalQuantity {

    DISTANCE,
    // others...

}

The Unit interface:

public interface Unit {

    double convert(double value);

}

The DistanceUnit implementing the Unit interface:

public enum DistanceUnit implements Unit {

    KILOMETER(R.string.unit_km, "km"),
    MILES(R.string.unit_mi, "mi");

    public static final double KM_PER_MI = 1.609344d;

    private int label;
    private String id;

    DistanceUnit(int label, String id) {
        this.label = label;
        this.id = id;
    }

    @Override
    public double convert(double meters) {
        double km = meters / 1000d;
        if (this == MILES) return km / KM_PER_MI;
        return km;
    }
}

The Problem:

The exception is thrown the first time I'm using the units:

Unit distanceUnit = UnitPreferences.from(context).getPreferredUnit(DISTANCE);

When I implemented it everything worked fine, now after it was merged into the master I'm getting a VerifyError

java.lang.VerifyError: Verifier rejected class com.example.app.UnitPreferences: com.example.app.Unit[]
  com.example.app.UnitPreferences.getAllUnits(com.example.app.PhysicalQuantity) failed to verify:
  com.example.app.units.Unit[] com.example.app.UnitPreferences.getAllUnits(com.example.app.PhysicalQuantity):
  [0x28] returning 'Reference: java.lang.Enum[]', but expected from declaration 'Reference: com.example.app.Unit[]'
  (declaration of 'com.example.app.UnitPreferences' in /data/app/com.example.app-2/base.apk:classes32.dex)

I've already cleaned and rebuild several times and turned off Instant Run. Can anyone give me a hint how to fix this error?

mbo
  • 4,611
  • 2
  • 34
  • 54

2 Answers2

2

Just looked into the stacktrace a little more and it says:

returning 'Reference: Enum[]', but expected from declaration 'Reference: Unit[]'

in getAllUnits()

Casting the returned enums of getAllUnits() to Unit[] manually fixed the issue although there is a hint:

Casting 'DistanceUnit.values()' to 'Unit[]' is redundant

Casting Unit[] manually

mbo
  • 4,611
  • 2
  • 34
  • 54
2

This part seems key:

returning 'Reference: java.lang.Enum[]', but expected from declaration 'Reference: com.example.app.Unit[]

So you are returning an enum array when you should be returning a unit array. Change either the return type of the method, or simply pack the DistanceUnit values into a list to fix the problem.

I would recommend using List<Unit> as your return type instead of Unit[]. See this for reference. To do so, call Arrays.asList(DistanceUnit.values()) when instantiating your list.

Austin Schaefer
  • 695
  • 5
  • 19
  • 1
    Good point but if I use `Arrays.asList(DistanceUnit.values())` and change the return type to `List` I'm getting an error `Incompatible types: Required List - Found List`. So I have to cast it to `Unit[]` first and do `Arrays.asList((Unit[]) DistanceUnit.values())`. Then I rather use just the cast and keep `Unit[]` as return type. – mbo Jul 19 '17 at 06:14
  • Sounds good to me. Sorry I wasn't more thorough with checking such things in my answer; I don't have access to my personal Java environment while I'm at work. – Austin Schaefer Jul 19 '17 at 07:52