1

I have a program with various ID constants like

final int MN=8;
final int PY=58;
final int RB=13;
...

these costants are index of elements in a matrix, for various reasons difficult to explain, will be good If I can do a method that receives two strings for example

Object getElement("MN","PY"){
    ...
}

to acts like

 Object o=mymatrix[MN][PY];

using the passed strings to reference declared fields

Is there any way to do this accessing the fields directly (without puth them in a Map or use switch and if else condition for every field)?

AndreaF
  • 11,975
  • 27
  • 102
  • 168
  • Is there a pattern the letters and numbers follow? Can you just place them in an `enum` and refer to their `ordinal`? For example `enum x { AA, BB, CC, DD }` then making a call to `x.AA.ordinal()` would return 0, BB is 1, and so forth. – mrres1 Mar 06 '14 at 02:36
  • no, the names of fields are random – AndreaF Mar 06 '14 at 02:41
  • Can you post more of those constants? How many are there? You should place them in a `Map` at run time as a key/value pair. "MN" or "PY" being the key, 8 or 58 being the values. – mrres1 Mar 06 '14 at 02:46
  • the costants are 617. for this reason I'm looking for another way that doesn't force me to add all these in a Map or enumeration – AndreaF Mar 06 '14 at 02:49
  • 1
    It's easy, you can use a find/replace in a text editor to change it to a map. Add all 617 variables to www.pastebin.com and I will format it for you. – mrres1 Mar 06 '14 at 02:54
  • 1
    Slightly outside the square, but you could even copy/paste the whole lot into a properties file, and just search/replace the spaces to dots and the semicolons to nothing. Then use `properties.getProperty("final.int." + fieldName);` in your code. – Dawood ibn Kareem Mar 06 '14 at 03:07

4 Answers4

2

I think you can check this link out: Get variable by name from a String

There are four interesting suggestions there, using different approaches: Reflection, Map, Guava and Enum.

-------------- EDITED ANSWER ------------- (With contributions from David Wallace)

Please check the following program out:

import java.lang.reflect.Field;

public class Main
{

    public final static int MN = 0;
    public final static int PY = 1;
    public final static int RB = 2;

    private static int[][] myMatrix = new int[][]
    {
        { 1, 2, 3 },
        { 10, 20, 30 },
        { 100, 200, 300 }
    };

    public static void main( String[] args ) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException
    {
        System.out.println( getElement( "MN", "PY" ) );
        System.out.println( getElement( "PY", "RB" ) );
        System.out.println( getElement( "RB", "RB" ) );


    }

    public static int getElement( String i0, String i1 ) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException
    {
        return myMatrix[ getValueByName( i0 )] [ getValueByName( i1 ) ];
    }
    public static int getValueByName( String varName ) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException
    {

        Field f = Main.class.getField( varName );
        return f.getInt( f );
    }
}

The output is: 2, 30, 300. Does it respond your question?

Community
  • 1
  • 1
Almir Campos
  • 2,833
  • 1
  • 30
  • 26
  • thanks for answer but I'm looking for a way that don't obbligate me to put every variable in a Map or use switch. Is there no way to access fields directly? – AndreaF Mar 06 '14 at 02:29
  • You're welcome. Would you give an example with the code you want, please? – Almir Campos Mar 06 '14 at 02:34
  • Hi, AndreaF. I just edited my answer to give you a possible solution to the problem you presented. – Almir Campos Mar 06 '14 at 03:06
  • Better to use `getField` (as per the comment under my answer) than to iterate through all 617 of them. – Dawood ibn Kareem Mar 06 '14 at 03:08
  • Yes. It's much better. I'm editing the code. Thank you David Wallace. – Almir Campos Mar 06 '14 at 03:12
  • I see you've made the constants static, which may well be an improvement. But you've also made a lot of other stuff static, which the OP mightn't necessarily want. It's worth noting that if you don't do that, you'll need to write `f.getInt(this)` not `f.getInt(f)`, to make sure that the fields' values are taken from the correct object. It's probably better form to use `f.getInt(null)` to get a static field. – Dawood ibn Kareem Mar 06 '14 at 03:13
  • Of course the code needs a lot of improvements. I'm only showing an very specific example on how to use Reflection to get the results, to keep it simple. Your suggestions are very welcome and I think that AndreaF should follow them when implements his own solution. Thank you David. – Almir Campos Mar 06 '14 at 03:20
2

Here is an illustration of how you can do this using reflection.

public class Matrix {
    private Object[][] values = new Object[100][100];
    private final int MN=8;
    private final int PY=58;
    private final int RB=13;

    public static void main(String[] args){
        Matrix matrix = new Matrix();
        matrix.values[8][58] = "Hello";
        try {
            System.out.println(matrix.getElement("MN","PY"));
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }

    }

    public Object getElement(String rowConstant, String columnConstant) 
    throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
        return values[getConstant(rowConstant)][getConstant(columnConstant)];
    }

    private int getConstant(String constant) 
    throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
        return getClass().getDeclaredField(constant).getInt(this);
    }
}

See how in the main method, I put an object into the array, and then use reflection to retrieve it.

I entirely do not recommend this technique. I've only put this answer here because you specifically asked me for it. I really think you're going about this the wrong way.

If you have a small number of constants, you should be using a HashMap or some similar structure. If you really do have 617 of them, you should be using some kind of data file, or a properties file, or a database. There's no way you want a big long list of constants to be part of your source code.

Dawood ibn Kareem
  • 77,785
  • 15
  • 98
  • 110
1

There are lots of ways to do this, but by far the simplest is to put all your constants in a HashMap and use get whenever you want to refer to one.

Dawood ibn Kareem
  • 77,785
  • 15
  • 98
  • 110
  • thanks for suggewstion but I'm looking for a way that don't obbligate me to put every variable in a Map or use switch. Is there no way to access fields directly? – AndreaF Mar 06 '14 at 02:29
  • Well, you could use reflection, but it's rather more complicated and error-prone than just using a map. – Dawood ibn Kareem Mar 06 '14 at 02:33
  • I prefer take this risk because otherwise I have to write an huge number of codelines. XD could you give me more details on how to use reflection to do this? Thanks – AndreaF Mar 06 '14 at 02:36
  • Oh, you'll have to write much more code to do it with reflection, because there are all sorts of checked exceptions that just make things messy. I still advise using a map - it takes only one more line of code than declaring all the constants anyway, because you'll replace `final int MN=8;` with `constants.put("MN",8);` and the only extra line you need is the one where you create the map. I really can't fathom why you wouldn't want to do this. But if you want the longer and more complex reflection-based solution, I'll post it in a little while. – Dawood ibn Kareem Mar 06 '14 at 02:40
  • thanks for the advice but I would be really grateful if you let me see the reflection based solution. – AndreaF Mar 06 '14 at 02:46
  • OK. I'll post it in a little while. The core of it is something like `getClass().getField(fieldName).getInt(this);` but there are loads of exceptions it can throw. I need to post it as a separate answer to provide you with all the details. – Dawood ibn Kareem Mar 06 '14 at 02:56
0

Consider revisiting your original assumptions. Looking up variables by name at runtime is not a great approach, esp. in Java. It's making your task harder than necessary. Also more fragile -- variables may be renamed by processing tools such as Proguard.

Where did all the constant definitions final int MN=8; ... come from in the first place? If they're generated by code, then you'd be better off making it instead generate code that constructs a HashMap, or a data file that you can read into a HashMap. Or if you have to start with those constant definitions, write a little program that reads that source code text and constructs a HashMap.

The final int definitions aren't doing you much good in their current form.

Jerry101
  • 12,157
  • 5
  • 44
  • 63