是否可以在 Java 中在运行时创建变量?
Posted
技术标签:
【中文标题】是否可以在 Java 中在运行时创建变量?【英文标题】:Is it possible to create variables at runtime in Java? 【发布时间】:2011-11-20 16:27:36 【问题描述】:例如,假设我想将String[] fruits = "Pear", "Banana", "Apple";
“提取”成三个单独的变量,例如:
for (int i=0; i != fruits.length; ++i)
// of course there's no eval in Java
eval("String fruit + i = " + fruits[i] + ";");
// ie: code that creates something equivalent to the following declarations:
String fruit0 = "Pear";
String fruit1 = "Banana";
String fruit2 = "Apple";
我怎么能这样做,忽略“你为什么要这样做?”你可能会被敦促问我的问题。
以前曾多次提出类似的问题,但从未给出真正的答案,因为 OP 真正需要的是使用不同的方法。这很好,但这可能吗?
我查看了反射,似乎没有任何方法可以让我向实例添加额外的字段,更不用说动态创建局部变量了。
【问题讨论】:
我能想到的唯一可能的方法是使用运行时字节码注入。据我所知,我们无法在编译前创建动态变量。 让我猜猜,您主要是 php 程序员,不是吗?我接近了吗? 我认为java.lang.reflect.Proxy
是您可以去的最接近的地方。即使那样你也只能实现在编译时定义的东西。
是的,但您将无法在通过该机制创建的代码之外使用它们(除非界面一致)。
@HovercraftFullOfEels 我从来没有在 PHP/Python/JS 的实际代码中使用过它(在堆栈上创建新的局部变量),但事实是,如果我需要它,它就在那里。另一个特性是动态地向实例添加成员,但更有用。
【参考方案1】:
是否可以在 Java 中在运行时创建变量?
简单的答案是否定的。
Java 是一种静态语言,不支持将新的变量声明注入到现有的编译程序中。有替代方案(按有用性/难度增加的顺序):
在Map
中将“变量”表示为名称/值对。或者想出其他一些不需要需要真实动态变量的设计。
使用在 JVM 上运行并可从 Java 调用的脚本语言。
使用某种模板机制生成包含声明的新源代码,并动态编译和加载它。
使用字节码操作库(例如 BCEL)动态创建类文件,然后动态加载它们。
第一种方法是最好的。 Java 是一种静态语言,如果你不反对它,效果最好。如果这对您来说是个问题,可能是您使用了错误的语言。
最后两个是困难/复杂的,并且具有显着的性能成本。他们几乎肯定不会提供帮助......
【讨论】:
【参考方案2】:问题不是你为什么要这样做,而是“你打算用它做什么?”。所以假设在运行时变量名为fruits2
神奇地出现在你的方法的堆栈上。怎么办?您必须在编译时知道它的名称才能利用它。反射不会帮助您访问局部变量。
无论如何,如果您描述更详细的用例,我会很感兴趣。
【讨论】:
@Alex,如果你曾经使用过 javascript 的 eval,你会看到这个功能实际上是多么强大和有用。您可以根据各种因素即时构建整个代码段并执行它们,等等。如果您从未*以这种方式思考过,我意识到很难看到它的用途,但是一旦您了解了这些可能性,当您转向不支持它的语言时,您真的会错过它(即大多数)。 @Dr.Dredel 我完全同意你的看法。然而,Java 不是一种动态语言,只要我们的讨论范围仅限于 Java(问题是),我们就必须处理它。我肯定会在 Java 中找到类似 javascript 的eval
的良好用途,但它(遗憾的是)不存在。【参考方案3】:
您提出问题的方式,人们不会理解您在问什么。我相信(如果我理解的话)您的问题的答案(应该表述为:“是否可以在运行时动态创建变量”)是“不像您所提出的那样”。
你说得对,Java 中没有类似的 javascript(非常强大,但缓慢且充满危险的“eval”函数),而这正是你需要做的事情才能完成你希望做的事情做。
存在的最接近的是哈希图(实际上非常接近),您可以在运行时指定键,然后设置值。它的用途相当广泛,因为您可以拥有一个允许在字段中存储任何类型的地图。
【讨论】:
【参考方案4】:您将无法修改已加载到 JVM 中的类。但是,您可以想象使用 ASM http://asm.ow2.org/> 或 BCEL http://commons.apache.org/bcel/> 来动态生成具有动态定义字段的新类。
麻烦多于它的价值。说真的,只需使用 HashMap!
【讨论】:
【参考方案5】:Janino 对你有用吗?
这里有一些代码。我认为它接近你想要的,但我不确定。
package misc;
import java.lang.reflect.InvocationTargetException;
import org.codehaus.janino.CompileException;
import org.codehaus.janino.ScriptEvaluator;
import org.codehaus.janino.Parser.ParseException;
import org.codehaus.janino.Scanner.ScanException;
public class JaninoExample
public static void main(String[] args)
String in = " \"Pear\", \"Banana\", \"Apple\";";
try
ScriptEvaluator se = new ScriptEvaluator("return new String[]"+in,String[].class);
try
String[] fruits = (String[])se.evaluate(new Object[]);
for(String fruit:fruits)
System.out.println(fruit);
catch (InvocationTargetException e)
e.printStackTrace();
catch (CompileException e)
e.printStackTrace();
catch (ParseException e)
e.printStackTrace();
catch (ScanException e)
e.printStackTrace();
【讨论】:
【参考方案6】:您能否详细说明,不确定您在此处所做的不同之处。当然,您可以创建三个不同的字符串。但是我相信 java 中的语法是 string xx = new string("DDFD");
编辑:
我的意思是,你想在这里改变什么。您可以动态分配内存,因此您可以动态创建“变量”。但是,您不能以原始方式创建“变量”,例如“int x = 0;”在运行时,您可以在运行时将节点添加到链表、调整数组大小等。
【讨论】:
我相信我有一个模糊的想法,我相信有人提到了“字节注入”,不确定这是否是我正在考虑的,因为我对此有最小的概念。但是,如果您可以找出散列内存和堆栈内存中的内存地址位置,则可以通过超出参数等范围来直接将变量名称与其相关联。虽然我对此知之甚少,但这就像缓冲区溢出的整个概念黑客攻击。我认为这可能是您想要研究的方向?以上是关于是否可以在 Java 中在运行时创建变量?的主要内容,如果未能解决你的问题,请参考以下文章