6

假设我需要生成变量来保存用户的一些输入(我不知道它们有多少)。如果不使用Array, ArrayList(和其他类型的列表和地图),我的代码可以生成(比如说)String变量 X 次,名称为(String var001、、、String var002String var003)吗?如果是,请提供示例代码。

4

10 回答 10

5

如果你真的想做这样的事情,你可以通过使用ASM或其他库生成字节码来实现。

下面的代码将生成一个名为“foo.bar.ClassWithFields”的类,其中包含字段“var0”到“var99”。当然,除了反射之外别无他法来访问这些字段,因为它们在编译时不存在,而 Java 是一种静态类型的语言。

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);
        }
    }
}
于 2009-07-28T14:19:23.847 回答
4

不使用 Array、ArrayList(以及其他类型的列表和映射)

创建具有这些名称的文件。希望这对你的教授有用。

或者使用前面提到的 Java Scripting API:

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"

编辑

似乎在内部这将使用 Maps :) 与属性文件、首选项 API 或 DOM 树相同(它们使用向量)。因此,如果您的教授如此挑剔,请使用文件。

于 2009-07-28T10:39:45.600 回答
3

我还没有看到这个答案,所以我会去的。编写一个只写出 Java 源代码的程序。其中大部分可能是一个模板,而您只需有一个循环,可以根据需要编写任意数量的“字符串 UserString003”类型变量。

是的,这太可怕了。但是,正如您所说,这是作业的概念挑战问题,所以只要没有人将其误认为“好”代码,它可能会解决问题。

于 2009-07-28T13:39:29.280 回答
2

你的意思是你想生成名为的变量

var0、var1、var2 并在您的代码中使用它们。

使用 var[0]、var[1]、var[2]、.....

您可以在运行时动态生成 Java 类,该类实现您在普通代码中使用的接口。然后使用编译器(例如 Janino)编译该类,然后在运行时加载该类。比你动态地创建了一个类。

但我想知道,这对于您的用例是否是必要的。

编辑

我现在不针对哪个用例使用此参数,但您可以在 Java 中使用动态参数,例如此处的示例

// 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
于 2009-07-28T08:06:36.173 回答
2

像这样命名变量看起来很像 1980 年。意思是面向对象编程之前。因此,如果您以构建软件为生 - 不要这样做。

但因为它似乎是家庭作业......

当我们谈论 Java 中的命名变量时,我们指的是已编译的东西。与某些脚本语言不同,在 Java 中没有简单的方法可以做到这一点。

因此,您要么使用像 Markus Lausberg 建议的运行时编译类。
或者您作弊并使用Java Scripting API并利用脚本语言。这样您就可以在运行时创建代码(在字符串中)。

于 2009-07-28T09:29:34.257 回答
2

我认为您可以在运行时生成一个 Java 类,或者使用Beanshell之类的脚本引擎来生成变量,您甚至可以通过其字节码构建该类。但是我看不到您将如何在代码中使用该变量,您还必须创建代码以使用该变量,或者为此使用反射...

一个天真的解决方案:
创建一个包含从 var000 到 var999 的所有变量的类,每个变量都有一个吸气剂......但这并不是真正动态的!

于 2009-07-28T09:47:06.203 回答
2

看起来你的教授在这个特性(变量变量)上偏向 PHP,所以他在想这在 java 中是否可行。

我个人认为这是不可能的,而不是你提议的方式。可以做的是在运行时生成类,使用Javassist之类的工具来制作更强大的反射机制。因此,您可以在运行时创建一个具有所需变量(string1、string2 等)的类。

但是,不要忘记变量变量是一种非常糟糕的技术,它会导致糟糕的代码。它可能在极少数情况下有用,但我真的不推荐它。

于 2009-07-28T14:29:08.137 回答
2

以下是我实施并帮助我轻松解决我的解决方案的方式,没有太多障碍。

// 创建数组列表

List accountList = new ArrayList(); 




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

迭代循环并将对象添加到带有索引的数组列表中。

//在运行时借助索引检索对象

String a = accountList.get(i));
于 2011-05-11T10:57:00.577 回答
1

这是不可能的,但这是使用其中一个 java 集合的理想选择。

要么使用动态分配的数组:

String[] arr = new String[RUNTIME_SIZE];

或者可以在运行时更改其大小的列表:

List list = new ArrayList<String>();
于 2009-07-28T08:06:41.930 回答
0

我不知道我是否正确理解了您,但是如果您尝试为变量使用动态创建的名称,那么是的,当然-我这样做是这样的:

// 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();

正如你所看到的,这里的关键点是符号“$”,它基本上表示“从这个符号之后给出的字符串创建变量名”,基本上就是这样 - 几年来对我来说就像一个魅力...... .希望这是你想要的,它有助于解决你的问题。

于 2018-09-12T19:19:43.873 回答