ユーザーからの入力を保持するために変数を生成する必要があるとしましょう(変数の数はわかりません)。Array
、ArrayList
(および他の種類のリストとマップ)を使用せずに、私のコードは( 、、、など)String
のような名前の変数をX回生成できますか?はいの場合、サンプルコードを提供してください。String var001
String var002
String var003
10 に答える
本当にそのようなことをしたい場合は、 ASMまたは他のライブラリを使用してバイトコードを生成することで実行できます。
フィールド「var0」から「var99」を含む「foo.bar.ClassWithFields」という名前のクラスを生成するコードを次に示します。もちろん、コンパイル時には存在せず、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);
}
}
}
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"
編集
内部的にこれはマップを使用するようです:)プロパティファイル、設定API、またはDOMツリーと同じです(それらはベクトルを使用しています)。したがって、教授がうるさい場合は、ファイルを使用してください。
私はまだこれが答えられるのを見たことがないので、私はそれのために行きます。Javaソースコードを書き出すだけのプログラムを書いてください。そのほとんどはテンプレートである可能性があり、必要な数の「文字列UserString003」型変数を書き込むループがあります。
はい、これはひどいです。しかし、あなたが言ったように、それは宿題の概念的な挑戦の問題です、誰もこれを「良い」コードと間違えない限り、それは問題を解決するかもしれません。
そのような名前付け変数は非常に1980年っぽく見えます。プレオブジェクト指向プログラミングを意味します。したがって、生計を立てるためのソフトウェアを構築する場合は、これを行わないでください。
でも宿題のようですので…
Javaで名前付き変数について話しているときは、コンパイルされたものを意味します。一部のスクリプト言語とは異なり、Javaでこれを行う簡単な方法はありません。
したがって、MarkusLausbergが提案したようなランタイムコンパイル済みクラスを使用します。または、 Java Scripting API
をだまして使用し、スクリプト言語を利用します。そうすれば、実行時に(文字列で)コードを作成できます。
以下は、私が実装し、多くのハードルなしで簡単にソリューションを修正するのに役立った方法です。
// 配列 List の作成
List accountList = new ArrayList();
for(int k=0;k < counter;k++){
accountList.add(k, (String)flowCtx.getValueAt("transitId"+m));
}
ループを繰り返し、インデックスを使用してオブジェクトを arraylist に追加します。
//インデックスを使用して実行時にオブジェクトを取得する
String a = accountList.get(i));
実行時に Java クラスを生成するか、Beanshellなどのスクリプト エンジンを使用して変数を生成できると思います。バイトコードでクラスを構築することもできます。しかし、コードでその変数をどのように使用するかわかりません。その変数を操作するコードを作成するか、そのためにリフレクションを使用する必要があります...
単純な解決策:
var000 から var999 までのすべての変数を持つクラスを作成し、それぞれにゲッターを使用します... しかし、それは実際には動的ではありません!
あなたの教授は、この機能 ( Variable variables ) について PHP に偏っているように見えるので、Java でそれが可能かどうかを考えていました。
個人的には、あなたが提案している方法では、これは可能ではないと思います。できることは、Javassistなどのツールを使用してより強力なリフレクション メカニズムを作成し、実行時にクラスを生成することです。したがって、実行時に必要な変数 (string1、string2 など) を持つクラスを作成できます。
ただし、 Variable 変数は悪いコードにつながる非常に悪い手法であることを忘れないでください。非常にまれなケースで役立つかもしれませんが、私は本当にお勧めしません.
という名前の変数を生成したいということです
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
これは不可能ですが、Java コレクションの 1 つを使用するのに最適な候補です。
動的に割り当てられた配列を使用します。
String[] arr = new String[RUNTIME_SIZE];
または、実行時にサイズを変更できるリスト:
List list = new ArrayList<String>();