简体   繁体   中英

Constant string substitution

I have a class of constants

public class Constants {
    private static final String A = "1";
    private static final String B = "2";
}

I have another class which takes in the name of the constant as a function parameter and calls the constant.

public class SomeClas {
    void someMethod(String constantName) {
        callSomeOtherMethod(Constants.<constantName>)
    }
}

How Do i do this? My <constantName> can take values as A or B .

Assuming you cannot change anything in the way your classes look like, you are left with reflection. The code to do it with reflection is as follows:

void someMethod(String constantName) throws NoSuchFieldException, IllegalAccessException {
    Field fd = Constants.class.getDeclaredField(constantName);
    fd.setAccessible(true);
    String val = (String) fd.get(null);
    callSomeOtherMethod(val);
}

The answer depends on how much control you have of the class Constants . If this is out of your control and you cannot change it then reflection is the way to go. (see marcinj's answer )

However, if you have full control over Constants then I would consider refactoring to an enum (available since Java 5). Whether this is worthwhile will depend on how embedded this class is in your code base. How many places that reference Constants would have to change? Is this a shared class used by other applications? It could be that refactoring here is too much hassle, only you can decide.

To help you decide here is a summary of reasons why using an enum would generally be considered preferable, certainly for new development. If you decide not to refactor then it's still worth a look for the next time you need to create new constants like this.

Reasons against using reflection

  • Performance - runtime reflection is much slower than compiled method calls or attribute lookups. If your code is called infrequently then you probably won't notice it but if this is a utility method that is called many times then it could be a potential bottleneck.
  • Overriding the access modifier - private scope attributes are supposed to only be accessible from within the same class. By overriding this you can introduce problems when refactoring as your reflection code could be dependent on attributes or methods that it shouldn't know about.
  • Compile time safety - if you call a method or reference an attribute in the standard way the compiler will check it exists. If you look things up with reflection then you leave yourself open to runtime errors.

Reasons to prefer an enum to String/int constants
- Each constant can have attributes and methods - Using the Joshua Bloch example you might have a constants class listing the planets of the solar system. If you use an enum type then you can add attributes such as mass, radius etc and methods to retrieve them.
- Compile time type safety - With a class of String constants if you want to pass this in to a method the type will be String, not Constants. This means the compiler will be happy with any old String you pass in whether it's a Constant or not. If you use an enum you have a proper type that the compiler can check.
- You get lots for free such as name(), valueOf(), implements Serializable, Comparable etc. This means you don't have to re-invent the wheel.
- It's a thought out design - Before enums there were a number of design patterns to achieve the same thing with varying levels of considerations. For example do you worry about thread safety? Or Serialization? If you use an enum you don't have to worry about this.

Code example
If you do decide to refactor to an enum then here is an example code snippet to show how this might be achieved.

public enum Constant
{
    A("1"),
    B("2");

    private String value;

    private Constant(String value)
    {
        this.value = value;
    }

    public Constant lookupConstantByValue(String value)
    {
        for(Constant constant : values())
        {
            if(constant.value.equals(value))
            {
                return constant;
            }
        }
        return null;
    }
}

You could now lookup constant values either by the A,B name or the "1", "2" value. eg

public class SomeClas 
{
    void someMethod(String constantName) 
    {
        // if constantName is 1 or 2
        callSomeOtherMethod(Constant.lookupConstantByValue(constantName));
        // if constantName is A or B
        callSomeOtherMethod(Constant.valueOf(constantName));
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM