6

Let's say that I need to generate variables to hold some input from the user (I don't know how many they are). Without using Array, ArrayList (and other kind of lists and maps) can my code generate (lets say) String variables X times with names like (String var001, String var002, String var003, etc)? If yes, please provide sample code.

Mike
  • 47,263
  • 29
  • 113
  • 177
M. A. Kishawy
  • 5,001
  • 11
  • 47
  • 72
  • 6
    This is a very weird request... what do you mean - "don't know how many there are"? When will you know? At runtime? Anyway, this really looks like a job for a collection class. Why can't you use them? Could you please tell us more about your problem? – Vilx- Jul 28 '09 at 08:02
  • I know it's a weird request, but this is a challenging problem our professor is putting on table. So I'm concerned more with the possibility of doing it regardless of its usability! The only trick around this that my group found is what "Markus Lausberg" already suggested regarding this issue...so is there other way around it? – M. A. Kishawy Jul 28 '09 at 08:37
  • 2
    I hope your professor doesn't read stack overflow! What's his/her name? – Rob Fonseca-Ensor Jul 28 '09 at 08:50
  • LoL, don't worry "Rob"...he doesn't mind us asking for help as long as we credit the source. (No Names Please :) – M. A. Kishawy Jul 28 '09 at 09:07
  • Are Hashtable forbidden? – Nicolas Jul 28 '09 at 09:14
  • No Hashtables! It's not allowed! – M. A. Kishawy Jul 28 '09 at 09:23
  • 3
    I think your professor meant reflection. I never used Java reflection, but looking through the API, you don't associate identifiers to object instances created by reflection in Java. Surely there must be some misunderstanding here. – Tamas Czinege Jul 28 '09 at 09:34
  • I never heard of this "reflection" API before, so thanks for the new info "DrJokepu". I will check it out and see. – M. A. Kishawy Jul 28 '09 at 10:23
  • 2
    Ask your professor if he'd like you to program blindfolded as well - this makes about as much sense. Arrays and collections exist for exactly this purpose. I don't even see how you'd learn anything useful from such an exercise. – Michael Borgwardt Jul 28 '09 at 11:48
  • :) Take it easy "Michael"...I think there is idea behind this, maybe something like (Although you can use calculator, you should know how to multiply and divide manually)...or who knows...The only thing I know is "it's a problem, and I welcome all suggested solutions. Thanks for your comment! – M. A. Kishawy Jul 28 '09 at 12:20

10 Answers10

5

If you really want to do something like that, you can do it through bytecode generation using ASM or some other library.

Here is code that will generate a class named "foo.bar.ClassWithFields" that contains fields "var0" to "var99". Of course there is no way other than reflection to access those fields, because they don't exist at compile time and Java is a statically typed language.

import org.objectweb.asm.*;
import static org.objectweb.asm.Opcodes.*;

import java.lang.reflect.Field;

public class GeneratedFieldsExperiment {

    public static byte[] generateClassWithFields(int fieldCount) throws Exception {
        ClassWriter cw = new ClassWriter(0);
        FieldVisitor fv;
        MethodVisitor mv;
        AnnotationVisitor av0;

        cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, "foo/bar/ClassWithFields", null, "java/lang/Object", null);

        for (int i = 0; i < fieldCount; i++) {
            fv = cw.visitField(ACC_PUBLIC, "var" + i, "Ljava/lang/String;", null, null);
            fv.visitEnd();
        }
        {
            mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
            mv.visitCode();
            mv.visitVarInsn(ALOAD, 0);
            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
            mv.visitInsn(RETURN);
            mv.visitMaxs(1, 1);
            mv.visitEnd();
        }
        cw.visitEnd();

        return cw.toByteArray();
    }

    public static void main(String[] args) throws Exception {
        MyClassLoader loader = new MyClassLoader();
        Class<?> c = loader.defineClass("foo.bar.ClassWithFields", generateClassWithFields(100));

        System.out.println(c);
        System.out.println("Fields:");
        for (Field field : c.getFields()) {
            System.out.println(field);
        }
    }

    private static class MyClassLoader extends ClassLoader {
        public Class<?> defineClass(String name, byte[] b) {
            return defineClass(name, b, 0, b.length);
        }
    }
}
Esko Luontola
  • 73,184
  • 17
  • 117
  • 128
  • 2
    It uses the ASM library to generate a class - the byte array is in the same format as the .class files that Java compiler produces - and then loads it to the JVM using a custom class loader. ASM works at the Java bytecode level (similar to assembly code), so to understand the above code you first need to learn some Java bytecode (ASM's documentation is good for that: http://download.forge.objectweb.org/asm/asm-guide.pdf). Some other bytecode manipulation libraries may be easier to use than ASM, because they are higher level than pure bytecode. I think Javassist is one such library. – Esko Luontola Jul 28 '09 at 19:58
4

Without using Array, ArrayList (and other kind of lists and maps)

Create files with these names. Hope that will work for your professor.

Or use the Java Scripting API mentioned before:

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");

engine.put("x", "hello"); // you can add any variable here
// print global variable "x"
engine.eval("println(x);");
// the above line prints "hello"

EDIT

Seems like internally this will use Maps :) Same with Properties file, Preferences API, or DOM Trees (they are using Vectors). So if your professor is so picky, use files.

Denis Tulskiy
  • 19,012
  • 6
  • 50
  • 68
3

I haven't seen this answered yet, so I'll go for it. Write a program that just writes out Java source code. Most of it could be a template, and you would just have a loop that would write as many "string UserString003" type variables as you want.

Yes, this is horrible. But, as you said, it's a conceptual challenge problem for homework, so as long as no one mistakes this for "good" code, it might solve the issue.

Beska
  • 12,445
  • 14
  • 77
  • 112
2

You mean you want to generate variables named

var0, var1, var2 and use them in your code.

What is the difference when you use var[0], var[1], var[2], .....

BUT

You can generate a Java class dynamically at runtime which implements an Interface you are using in your normal code. Then you compile this class using a compiler (For example Janino) and then load the class at runtime. Than you have created a class dynamically.

But i wonder, whether this is necessary for your usecase.

EDIT

I dont now for which usecase you are using this parameters but dynamic arguments you can use in Java like this example from here

// calculate average
        public static double average( double... numbers )
        {
           double total = 0.0; // initialize total

          // calculate total using the enhanced for statement
          for ( double d : numbers )              
             total += d;                          

          return total / numbers.length;
       } // end method average
Rich
  • 3,928
  • 4
  • 37
  • 66
Markus Lausberg
  • 12,177
  • 6
  • 40
  • 66
2

Naming variables like that looks very 1980-ish. Meaning pre object oriented programming. So if you ever build software for a living - DON'T DO THIS.

But since it seems to be homework...

When we're talking about a named variable in Java, we mean something that's compiled. Unlike in some scripting languages there is no easy way to do this in Java.

So either you use a runtime compiled class like Markus Lausberg suggested.
Or you cheat and use the Java Scripting API and make use of the scripting languages. That way you can create code (in a String) at runtime.

Stroboskop
  • 4,327
  • 5
  • 35
  • 52
2

I think you can generate a Java class at runtime or maybe use some script engine like Beanshell to generate the variables, you can even build the class by its bytecode. But I can't see how you will use that variables in your code, you must also create the code to work with that variables, or use reflection for that...

A naive solution:
create a class with all variables from var000 to var999 with a getter for each... but that's not really dynamically!

user85421
  • 28,957
  • 10
  • 64
  • 87
2

It looks like your professor is PHP-biased on the feature (Variable variables), so he was thinking if that was possible in java.

I personally don't think that this is possible, not in the way you are proposing. What can be done is the generation of classes at runtime, using tools like Javassist to make a more powerful reflection mechanism. So you can create a class that has the variables you want (string1, string2, etc.) at runtime.

However, don't forget that Variable variables is a really bad technique, which leads to bad code. It might be useful on very few cases, but I really don't recommend it.

Gustavo Muenz
  • 9,278
  • 7
  • 40
  • 42
2

Following is the way that i have implemented and helped me to fix my solution easily without much hurdles.

// Creating the array List

List accountList = new ArrayList(); 




for(int k=0;k < counter;k++){
        accountList.add(k, (String)flowCtx.getValueAt("transitId"+m));
}

Iterating the loop and adding the objects into the arraylist with the index.

//Retrieving the object at run time with the help of the index

String a = accountList.get(i));
gmhk
  • 15,598
  • 27
  • 89
  • 112
1

This is not possible, but this is a perfect candidate for using one of the java collections.

Either use a dynamically allocated array:

String[] arr = new String[RUNTIME_SIZE];

Or a list which can change it's size during runtime:

List list = new ArrayList<String>();
Yuval Adam
  • 161,610
  • 92
  • 305
  • 395
0

I do not know if I understood you correctly but if you are trying to use dynamically created names for your variables then yes, definitely - I am doing it like this:

// rndRng() creates random numbers in specified range
// this would output dynamically created variable like "name89"
String myDynamicalyCreatedName = "name" + Utils.rndRng(0, 100);
final UberShader $myDynamicalyCreatedName = new UberShader();

As you can see the point key here is the sign "$" that basically says "create variable name from the String that is given after this sign", and that's basically it - works like a charm for me for a few years now...hope it is what you wanted and that it helps a bit solving your problem.

theoneiam
  • 11
  • 6
  • I don't think Java language works the way you think it does. You can eliminate the line where you define `myDynamicallyCreatedName` and the code will still do the same thing. It just creates a variable named `$myDynamicalyCreatedName`. – mrog Sep 12 '18 at 19:37
  • Well, strangely enough it definitely work: some of my code is creating materials (like colors with specific surface attributes - metallic, shiny, glass etc.), I have like 3 different colors of specific material (let's say "chrome"), each material must have unique name - now I am using the same script for all chrome materials and each of those created has its unique dynamically created name the way I described above...now tell me what is wrong as I see it work on daily basis! ;-) – theoneiam Sep 12 '18 at 21:30
  • Maybe you're not using Java? If dynamically created names worked that way, you should be able to do this: `String name = "myVar"; int $name = 1; System.out.println(myVar);` But that won't even compile. – mrog Sep 12 '18 at 22:21
  • See, I am not JAVA guru so please do not give me this rethorical question, I do not know why or how but it definitely work for me - everything compile without a hitch and works like a charm, if it would not every new material of the same class (in this case Chrome) would overwrite the previous one which it does not: all have their own unique names or better said not names like .setName() but "names" when you initialize them (english is not my native language, sorry)...BTW I would try rather System.out.println($name); – theoneiam Sep 12 '18 at 22:40
  • I think what's happening is that you're creating a variable named `$myDynamicalyCreatedName`. The value of `myDynamicalyCreatedName` is irrelevant. In fact, `myDynamicalyCreatedName` doesn't even need to exist. – mrog Sep 12 '18 at 22:49
  • it needs to exist IMHO cos else you would not have those unique names, you would only have several names of "$" - going from "$myDynamicalyCreatedName" to "$" has no logic or at least I did not get what you mean, anyway it simply works: dynamic var names are created and it actualy does not matter to me that much if they stay for "$name89" or only "name89" as that sign "$" has no effect for my code. One way or another it is dynamical name - time to go to sleep, 01:30 at night here, bye. – theoneiam Sep 12 '18 at 23:22