0

私は個人的なプロジェクトに取り組んでいますが、うまくいけば、大きくて恐ろしいものに成長するでしょう...

完全修飾されていなくても、文字列が参照するクラスを派生させたいと考えています。可能な限り JVM の動作を再現したいと考えています。たとえば、コードでは、インポートされたものや同じパッケージにある修飾されていないものを参照できます。これが完全に可能ではないことは理解していますが、試してみたいと思います。私の現在のコードには次の問題があります。

  • 遅いです。存在するすべてのクラスのリストを生成し、その中で一致を探します。 私のマシンでは約6秒かかります。 更新:クラスの代わりに一連のクラス名を取得するようにリフレクション コードを変更しました。現在の約2倍の速さ。
  • 明らかに、これは私が重複した問題を抱えている可能性があることも意味します. Javaが物事を明確にするために使用するのと同じ方法でリストを並べ替える方法はありますか? 更新これは残っている唯一の深刻な問題です。少なくとも、あいまいさが見つかった場合は例外をスローします。
  • Google Reflection ライブラリを使用して、クラス リストを取得します。と を使用ClasspathHelper.forClassLoader()していますClasspathHelper.forJavaClassPath()が、まだ不足している可能性があります。 更新良好なカバレッジを提供すると思われる 4 つの異なるクラス セットを統合しています。
  • 私は確かにjava.*がありません。これは、これらのクラスが原始クラスローダーによってロードされ、ハードコーディングできるためです。したがって、それらは利用できませんか?最後の手段として、java.* のクラスのリストを機械可読形式で提供して、検索に使用できるものはありますか? これまでのところ、 UPDATE http://docs.oracle.com/javase/7/docs/api/overview-summary.htmlが最良の選択肢のようです。どうやら、Javadoc を機械可読形式で出力するドックレットをコーディングすることもできたようです。すでにありますか?

他に何か問題はありますか?現在のコードは次のとおりです。

/**
 * Attempts to return the class specified by a string even if the it is not
 * fully qualified.  It does this by going through all the classes there are.
 * Note: You may specify arrays in normal declaration form, e.g. myArray[][].
 * 
 * @param classString       The string rep of the class/type
 * @return                  The class I think it refers to
 */
private static Class<?> getClassOfString(String classString) {
    classString = convertTypeToCanonicalForm(classString);
    Matcher matcher = Pattern.compile("(\\[+(\\w))?((\\w+(\\.\\w+)*);?)?").matcher(classString);
    matcher.find();
    String arrayPrefix = matcher.group(2);
    String className = matcher.group(4);
    try {
        if (arrayPrefix == null || arrayPrefix.equals("L")) {
            String classFound = null;
            for (String clazz : getSetOfAllClasses()) {
                if (clazz != null) {
                    if (clazz.matches("(.*\\.)?"+Pattern.quote(className)))
                        if (classFound == null)
                            classFound = clazz;
                        else
                            throw new RuntimeException("Class name '" + className +
                                    "' is ambiguous: " + classFound + " vs " + clazz);
                }
            }
            if (classFound != null) {
                classString = classString.replaceAll(Pattern.quote(className), classFound);
                return Class.forName(classString);
            }
        }
    } catch (ClassNotFoundException e) {
        throw new RuntimeException();
    }

        if (arrayPrefix == null)
            if (className == "boolean") return(boolean.class);          // primitive types
            else if (className == "byte") return(byte.class);
            else if (className == "char") return(char.class);
            else if (className == "double") return(double.class);
            else if (className == "float") return(float.class);
            else if (className == "int") return(int.class);
            else if (className == "long") return(long.class);
            else if (className == "short") return(short.class);
            else if (className == "void") return(void.class);

        // hack for java.* types-- look 'em up
        if (className != null) {
            String prefixFound = null;
            for (String prefix : javaTypes.keySet())
                for (String type : javaTypes.get(prefix))
                    if ((prefix+"."+type).matches(".*" + className))
                        if (prefixFound == null)
                            prefixFound = prefix;
                        else
                            throw new RuntimeException("Class name '" + className +
                                "' is ambiguous: java." + prefixFound + "." + className +
                                " vs java." + prefix + "." + className);
            if (prefixFound == null) prefixFound = "util";      // temp hack
            classString = classString.replaceAll(Pattern.quote(className), 
                    "java." + prefixFound + "." + className);
        }

        try {
            return Class.forName(classString);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("Class '" + className + 
                    "' is unknown or somewhere in java.* that I don't know about");
        }
}

/**
 * Converts the type from standard declaration form to canonical internal form
 * if necessary.  Practically this just means doing array translation.
 * 
 * @param type
 * @return
 */
private static String convertTypeToCanonicalForm(String type) {
    Matcher matcher = Pattern.compile("^(\\w+(\\.\\w+)*)((\\[\\])+)$").matcher(type);
    if (matcher.find()) {
        String typeTemp = matcher.group(1);
        if (typeTemp.equals("boolean")) typeTemp = "Z";         // primitive typeTemps
        else if (typeTemp.equals("byte")) typeTemp = "B";
        else if (typeTemp.equals("char")) typeTemp = "C";
        else if (typeTemp.equals("double")) typeTemp = "D";
        else if (typeTemp.equals("float")) typeTemp = "F";
        else if (typeTemp.equals("int")) typeTemp = "I";
        else if (typeTemp.equals("long")) typeTemp = "J";
        else if (typeTemp.equals("short")) typeTemp = "S";
        else typeTemp = "L" + typeTemp + ";";

        matcher = Pattern.compile("\\[\\]").matcher(matcher.group(3));
        while (matcher.find())
            typeTemp = "[" + typeTemp;
        type = typeTemp;
    }
    return type;
}

/**
 * List of package classes for each prefix in the java.* domain
 */
@SuppressWarnings("serial") 
static final Map<String, List<String>> javaTypes = new HashMap<String , List<String>>() {{
    put("lang",         Arrays.asList(new String[]{"Boolean","Byte","Character","Class","Double",
            "Float","Integer","Long","Short","String","Void"}));
    // rest of java.* goes here
}};

/**
 * Gets and stores a set of all the classes we can find.  Missing the java.* domain.
 * Uses the Google Reflection library.
 * 
 * @return          The class set
 */
static Set<String> classStringSet = null;
private static Set<String> getSetOfAllClasses() {

    if (classStringSet == null) {
        List<ClassLoader> classLoadersList = new LinkedList<ClassLoader>();
        classLoadersList.add(ClasspathHelper.contextClassLoader());
        classLoadersList.add(ClasspathHelper.staticClassLoader());                      
        classLoadersList.add(ClassLoader.getSystemClassLoader());                      
        Reflections reflections = new Reflections(new ConfigurationBuilder()
            .setScanners(new SubTypesScanner(false), new ResourcesScanner())
            .setUrls(ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[0]))));
        classStringSet = reflections.getStore().getSubTypesOf(Object.class.getName());

        reflections = new Reflections(new ConfigurationBuilder()
            .setScanners(new SubTypesScanner(false), new ResourcesScanner())
            .setUrls(ClasspathHelper.forJavaClassPath()));
        classStringSet.addAll(reflections.getStore().getSubTypesOf(Object.class.getName()));

    }

return classStringSet;
}

なぜ私はこれをしているのですか?楽しくなるよ!わかるでしょ。

4

1 に答える 1

1

遅いです。存在するすべてのクラスのリストを生成し、その中で一致を探します。私のマシンでは約6秒かかります。

実装は、すべてのディレクトリ、およびクラスパス / ブートクラスパス上のすべてのアーカイブのインデックスをトラバースしている可能性があります。(6秒は長すぎるように見えますが...クラスパスに非常に多くのクラスがある場合を除きます。)

毎回 6 秒かかる場合は、一連のクラス名を適切なデータ構造にキャッシュすることを検討する必要があります。

明らかに、これは私が重複した問題を抱えている可能性があることも意味します. Javaが物事を明確にするために使用するのと同じ方法でリストを並べ替える方法はありますか?

Java は名前のあいまいさを解消しません。衝突がある場合、それはコンパイル エラーです。

(実際には、私は単純化しすぎています。言語には、名前が処理されるという発言を管理する規則がありますが、これらの規則は、ソース コード内のimportステートメントに大きく依存します。ユースケースで import ステートメントに類似したものはありますか?)

私は確かにjava.*がありません。これは、これらのクラスが原始クラスローダーによってロードされ、ハードコーディングできるためです。したがって、それらは利用できませんか?

を使用してシステムクラスローダーを取得できるはずですgetSystemClassLoader()

于 2013-01-15T04:00:50.020 に答える