5

For the enum defined as below

public enum Cars {

    SWIFT("Maruti"),
    DZIRE("Maruti"), 
    CIAZ("Maruti"), 
    I10("Hyundai"), 
    CRETA("Hyundai");

    String company;
    Cars(String company){
        this.company = company;
    }   
}

There is Sonar error

Define a constant instead of duplicating this literal "Maruti" 3 times.

If we define static string, it fails to compile

Cannot reference a field before it is defined

Solution 1:

public enum Cars {

    SWIFT(CarsString.MARUTI.companyName),DZIRE("Maruti"), I10("Hyundai"), CRETA("Hyundai");

    static String MARUTI = "Maruti";
    String company;
    Cars(String company){
        this.company = company;
    }

    enum CarsString {
        MARUTI("maruti"),
        HYUNDAI("Hyundai");
        String companyName;
        CarsString(String companyName){
            this.companyName = companyName;
        }
    }
}

Solution 2:

Not sure if this is the way we do it. When it is not referenced by Cars.MARUTI, it errors out Cannot reference a field before it is defined.

public enum Cars {

    SWIFT(Cars.MARUTI),DZIRE(Cars.MARUTI), I10("Hyundai"), CRETA("Hyundai");

    public static final String MARUTI = "Maruti";
    String company;
    Cars(String company){
        this.company = company;
    }
 }

I have 10 enums in similar state and the above solution 1 didn't much encourage me. Solution 2 solves the purpose,

but why should we refer by Cars.MARUTI and not MARUTI directly? is that a limitation ?

How could we refer a constant value in enum ?

Vinay Veluri
  • 6,671
  • 5
  • 32
  • 56
  • Possible duplicate of [How to define static constants in a Java enum?](https://stackoverflow.com/questions/23608885/how-to-define-static-constants-in-a-java-enum) – Amit Bera Aug 29 '19 at 07:33
  • @AmitBera `but why should we refer by Cars.MARUTI and not MARUTI directly ? is that a limitation ?` This is the additional question that Im not aware of – Vinay Veluri Aug 29 '19 at 07:41
  • Please refer my answer for the explanation. Thanks. – Amit Bera Aug 29 '19 at 08:16

2 Answers2

5

What about defining the company in its own Enum? For example:

public enum Car {
    SWIFT(Company.MARUTI),
    DZIRE(Company.MARUTI),
    CIAZ(Company.MARUTI),
    I10(Company.HYUNDAI),
    CRETA(Company.HYUNDAI);

    Company company;

    Cars(Company company) {
        this.company = company;
    }
}

public enum Company {
    HYUNDAI("Hyundai"),
    MARUTI("Maruti");

    String name;

    Company(String name) {
        this.name = name;
    }
}

Update: improved naming conventions according to comment of @Basil Bourque, thx!

Mark Schäfer
  • 925
  • 1
  • 12
  • 23
  • 1
    Good Answer. A few suggestions. The enum should be named singular, `Car` rather than `Cars`. Each named element of an enum should be in all uppercase, by convention, so `MARUTI("Maruti")`. And a tip: At least in the United States, we commonly use the terms “Make” and “Model” for the car manufacturer and the particular kind is car. These might make better enum names. – Basil Bourque Aug 29 '19 at 07:37
  • If you see solution1, that is what is mentioned in the question. This is not about how to define an enum but how do we avoid String literals in enum constant values and feasible thing for existing application. Thanks – Vinay Veluri Aug 29 '19 at 07:44
2

You need to know how enum works internally. If you look at the byte code of your enum you will have the answer.

 6: iconst_0
   7: ldc           #19                 // String Maruti
   9: invokespecial #21                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
  12: putstatic     #25                 // Field SWIFT:Labc/Cars;
  15: new           #1                  // class abc/Cars
  18: dup
  19: ldc           #27                 // String DZIRE
  21: iconst_1
  22: ldc           #19                 // String Maruti
  24: invokespecial #21                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
  27: putstatic     #28                 // Field DZIRE:Labc/Cars;
  30: new           #1                  // class abc/Cars
  33: dup
  34: ldc           #30                 // String CIAZ
  36: iconst_2:

From the above byte code (which was generated from Cars enum), we can have a class which will be similar to the Cars enum

class Foo {
    public static Cars SWIFT = new Cars("Maruti");
    public static Cars DZIRE = new Cars("Maruti");
    public static Cars CIAZ = new Cars("Maruti");
}

Now if you change the code to :

class Cars {

    public static Cars SWIFT = new Cars(MARUTI); //statement-1
    public static Cars DZIRE = new Cars(MARUTI); //statement-2
    public static Cars CIAZ = new Cars(MARUTI); //statement-3

    public static String MARUTI = "Maruti"; // statement-4


    private String name;

    public Cars(String name) {
        this.name = name;
    }
}

It will give you the same error message because you are using the variable before it is defined. If we move up the statement-4 before statement-1 it will work. But it is only possible in class but enum does not allow you as the first statement.So in your case you can solve like :

enum Cars {

    SWIFT(Constants.MARUTI), DZIRE(Constants.MARUTI), I10("Hyundai"), CRETA("Hyundai");

    String company;

    Cars(String company) {
        this.company = company;
    }

    private static class Constants {

        public static String MARUTI = "Maruti";
    }
}
Amit Bera
  • 7,075
  • 1
  • 19
  • 42