当前位置: 首页 > 面试题库 >

如何通过类型签名搜索Java API方法?

农明辉
2023-03-14
问题内容

是否有可用的开源工具支持按参数类型和返回类型集搜索Java方法?

例如,假设我正在寻找一种为int数组生成哈希码的方法。我搜索一个采用int []参数并返回int的方法:

int[] -> int

屈服

java.util.Arrays#hashCode(int[])
...

或者,我可能想找到一种方法,该方法需要一个String,一个要替换的字符以及一个要替换为的字符。所以我寻找一个匹配的方法:

String, char, char -> String

屈服

java.lang.String#replace(char, char)
...

理想情况下,我想要一个与Haskell的Hoogle等效的Java ,该Java
支持按类型签名搜索功能。

我希望该工具能够:

  • 忽略参数的顺序
  • 包含接受“更广泛”类型作为参数的方法(例如,超类)
  • 包含返回“更窄”类型作为返回值的方法(例如子类)
  • 将’self’值视为实例方法的参数(例如’String-> int’将包含String#hashCode)

我知道许多IDE支持搜索采用或返回给定类型的方法,但是还没有看到一种通过参数类型 返回类型的组合来缩小搜索范围的工具。


问题答案:

解决方案评论:

  1. 忽略order ,测试所有参数的排列,直到找到给定函数的匹配项为止
  2. 基本类型可以互换(例如Integer.class= Integer.TYPE
  3. 检查更广泛的参数
  4. 返回类型可以更窄
  5. 自被视为 第一 的参数传递给findMethod方法

这是程序的输出:

int[] -> Integer
    public native int java.lang.Object.hashCode()
    public static native int java.lang.reflect.Array.getLength(java.lang.Object) throws java.lang.IllegalArgumentException
    public static int java.util.Arrays.hashCode(int[])
    public static native int java.lang.System.identityHashCode(java.lang.Object)

String, Character, Character -> String
    public java.lang.String java.lang.String.replace(char,char)

String -> Integer
    public int java.lang.String.hashCode()
    public int java.lang.String.length()
    public static native int java.lang.reflect.Array.getLength(java.lang.Object) throws java.lang.IllegalArgumentException
    public static java.lang.Integer java.lang.Integer.decode(java.lang.String) throws java.lang.NumberFormatException
    public static java.lang.Integer java.lang.Integer.valueOf(java.lang.String) throws java.lang.NumberFormatException
    public static int java.lang.Integer.parseInt(java.lang.String) throws java.lang.NumberFormatException
    public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String)
    public static native int java.lang.System.identityHashCode(java.lang.Object)

List -> Void
    public abstract void java.util.List.clear()
    public static void java.util.concurrent.locks.LockSupport.html" target="_blank">park(java.lang.Object)
    public static void java.util.Collections.reverse(java.util.List)
    public static void java.util.Collections.shuffle(java.util.List)
    public static void java.util.Collections.sort(java.util.List)

代码:

public class MethodMatcher {

    public static void main(String... args) throws Exception {

        // where to load some classes from (could be a list of classes to 
        // search from)..
        //        String pathToJar = "/usr/lib/jvm/java-6-sun-1.6.0.22/jre/lib/rt.jar";
        String pathToJar = "C:\\Program Files\\Java\\jdk1.6.0_20\\jre\\lib\\rt.jar";

        MethodMatcher m = new MethodMatcher(pathToJar, 
                "java.io", "java.lang", "java.math", "java.net", 
                "java.nio", "java.text", "java.util");

        // print some examples
        m.printExampleSearch(Integer.class, new int[0].getClass());
        m.printExampleSearch(String.class, String.class, Character.class, Character.class);
        m.printExampleSearch(Integer.class, String.class);
        m.printExampleSearch(Void.class, List.class);
    }

    public void printExampleSearch(Class<?> returnType, Class<?>... arguments) {

        for (int i = 0; i < arguments.length; i++)
            System.out.print((i == 0 ? "":", ") + arguments[i].getSimpleName());

        System.out.println(" -> " + returnType.getSimpleName());

        Set<Method> methods = findMethods(returnType, arguments);

        for (Method method : methods)
            System.out.println("\t" + method);

        System.out.println();
    }



    private final List<MethodFinder> klasses;

    public MethodMatcher(String jarFile, String... allowedPackages) 
    throws IOException, ClassNotFoundException {

        klasses = loadClasses(jarFile, allowedPackages);
    }

    /**
     * Finds a set of methods
     * @param returnType the return type
     * @param arguments the arguments (in any order)
     * @return a set of methods
     */
    public Set<Method> findMethods(Class<?> returnType,
            Class<?>... arguments) {

        Set<Method> methods = new LinkedHashSet<Method>();

        if (arguments.length > 0) {
            MethodFinder instance = new MethodFinder(arguments[0]);

            Class<?>[] rest = new Class<?>[arguments.length - 1];
            System.arraycopy(arguments, 1, rest, 0, rest.length);

            methods.addAll(instance.findInstanceMethods(returnType, rest));
        }
        else {
            for (MethodFinder k : klasses)
                methods.addAll(k.findInstanceMethods(returnType, arguments));
        }

        for (MethodFinder k : klasses)
            methods.addAll(k.findStaticMethods(returnType, arguments));

        return methods;
    }

    /**
     * A method finder class
     */
    static class MethodFinder {

        public final Class<?> klass;

        /**
         * Constructs the method finder (doh)
         * @param klass the class
         */
        public MethodFinder(Class<?> klass) {
            this.klass = klass;
        }

        /**
         * Finds instance method matches
         * @param returnType the return type
         * @param arguments the arguments (in any order)
         * @return
         */
        public List<Method> findInstanceMethods(Class<?> returnType, 
                Class<?>... arguments) {

            List<Method> matches = new LinkedList<Method>();

            for (Method method : klass.getMethods()) {
                if ((method.getModifiers() & Modifier.STATIC) == 0) 
                    if (testMethod(method, returnType, arguments))
                        matches.add(method);
            }

            return matches;        
        }

        /**
         * Finds static method matches
         * @param returnType the return type
         * @param arguments the arguments (in any order)
         * @return
         */
        public List<Method> findStaticMethods(Class<?> returnType,
                Class<?>... arguments) {

            List<Method> matches = new LinkedList<Method>();

            for (Method method : klass.getMethods()) 
                if ((method.getModifiers() & Modifier.STATIC) != 0) 
                    if (testMethod(method, returnType, arguments))
                        matches.add(method);

            return matches;        
        }

        /**
         * Tests a method if it is a match
         * @param method the method to test
         * @param returnType the return type
         * @param arguments the arguments (in any order)
         * @return true if it matches
         */
        private boolean testMethod(Method method, 
                Class<?> returnType, 
                Class<?>... arguments) {

            boolean returnTypeIsOk = false;
            for (Class<?> ic : getInterchangable(returnType))
                if (ic.isAssignableFrom(method.getReturnType()))
                    returnTypeIsOk = true;

            if (!returnTypeIsOk)
                return false;

            Class<?>[] methodArguments = method.getParameterTypes();

            if (methodArguments.length != arguments.length)
                return false;

            if (methodArguments.length == 0) {
                return true;
            }
            else {
                Permutations permutations = new Permutations(arguments);

                outer: for (Class<?>[] permutation : permutations) {
                    for (int i = 0; i < methodArguments.length; i++) {

                        boolean canAssign = false;
                        for (Class<?> ic : getInterchangable(permutation[i])) 
                            if (methodArguments[i].isAssignableFrom(ic))
                                canAssign = true;

                        if (!canAssign)
                            continue outer;
                    }
                    return true;
                }

                return false;
            }
        }

        /**
         * Returns the autoboxing types
         * @param type the type to autobox :)
         * @return a list of types that it could be
         */
        private static Class<?>[] getInterchangable(Class<?> type) {

            if (type == Boolean.class || type == Boolean.TYPE)
                return new Class<?>[] { Boolean.class, Boolean.TYPE };
            if (type == Character.class || type == Character.TYPE)
                return new Class<?>[] { Character.class, Character.TYPE };
            if (type == Short.class || type == Short.TYPE)
                return new Class<?>[] { Short.class, Short.TYPE };
            if (type == Integer.class || type == Integer.TYPE)
                return new Class<?>[] { Integer.class, Integer.TYPE };
            if (type == Float.class || type == Float.TYPE)
                return new Class<?>[] { Float.class, Float.TYPE };
            if (type == Double.class || type == Double.TYPE)
                return new Class<?>[] { Double.class, Double.TYPE };
            if (type == Void.class || type == Void.TYPE)
                return new Class<?>[] { Void.class, Void.TYPE };

            return new Class<?>[] { type };
        }


        /**
         * Creates a permutation list of all different combinations
         */
        @SuppressWarnings("serial")
        private class Permutations extends LinkedList<Class<?>[]> {

            /**
             * Creates a permutation list
             * @param list the list to be permutated
             */
            public Permutations(Class<?>[] list) {
                permutate(new LinkedList<Class<?>>(Arrays.asList(list)),
                        new LinkedList<Class<?>>());
            }

            // ugly, there is better ways of doing this...
            private void permutate(List<Class<?>> tail, List<Class<?>> choosen) {

                if (tail.isEmpty()) {
                    add(choosen.toArray(new Class<?>[0]));
                    return;
                }

                ListIterator<Class<?>> it = tail.listIterator();

                while (it.hasNext()) {

                    Class<?> current = it.next();

                    choosen.add(current);
                    it.remove();

                    permutate(new LinkedList<Class<?>>(tail), choosen);

                    choosen.remove(current);
                    it.add(current);
                }
            }
        }
    }

    /**
     * A hack to read some classes from some allowed packages
     * @param jarFile the jar file to read from
     * @param allowedPackages the allowed packages
     * @return a list of MethodFinders
     * @throws IOException
     * @throws ClassNotFoundException
     */
    private static List<MethodFinder> loadClasses(
            String jarFile, 
            String... allowedPackages) throws IOException, ClassNotFoundException {

        List<MethodFinder> klasses = new LinkedList<MethodFinder>();

        JarFile file = new JarFile(jarFile);
        try {
            Enumeration<JarEntry> enumerator = file.entries();

            while (enumerator.hasMoreElements()) {

                String name = enumerator.nextElement().getName();

                if (!name.endsWith(".class")) 
                    continue;

                name = name.substring(0, name.length() - 6).replace('/', '.');

                boolean allowed = false;
                for (String pkg : allowedPackages)
                    allowed |= name.startsWith(pkg);

                if (allowed)
                    klasses.add(new MethodFinder(Class.forName(name)));
            }
        } 
        finally {
            if (file != null)
                file.close();
        }

        return klasses;
    }
}


 类似资料:
  • 问题内容: 我想构建一个由Instagram照片驱动的小型“即时图片搜索”应用。就像Google Instant,您可以在其中开始键入内容,并且在键入更多字母时显示/更新结果。 您可以看到由Google图片提供支持的实时演示应用。我知道使用大型引擎(Google,Yahoo!,Bing)是可行的,但我不知道Instagram是否允许这样做。任何熟悉API的人都可以提供帮助吗? 如果无法通过有意义的

  • 问题内容: 我有这样的JPA实体类: 我需要这样的搜索方法 用法示例: 我对findAll方法的想法是 但这太丑陋了,看起来有些开销。谁能帮助我更礼貌地邀请该方法?我可以使用任何Java技术或框架。 问题答案: 所以您要寻找 原型 吗?Hibernate为此提供了一个方便的标准,因此,如果您不介意将自己与Hibernate API捆绑在一起,请尝试以下来自docs的示例: 它具体说: 版本属性,标

  • 问题内容: 我的脚本来调用ajax 的HTML 问题: 在onkeyup上,我正在使用ajax来获取结果。一旦ajax结果延迟增加,对我来说就会发生问题。 例如, 当键入关键字时,我收到ajax结果,而当键入两次键之间的ajax时间延迟有时引起严重问题时,我收到ajax结果。 当我快速打字时。与相比,ajax搜索关键字的时间晚了。我不知道该如何处理这类案件。 结果由于ajax延迟 而快速键入关键字

  • 在springboot中,您可以拥有一个

  • 请帮帮我!导航到某个页面后,selenium webdriver应按control F并使用robot类搜索数字。我尝试过使用actions类,但是不起作用。所以,我使用了机器人类。 正在工作。但是,如何发送我想要搜索/查找的号码。 另外,如果可能,请告诉我如何使用模式集从页面中搜索特定字符串。 更新:dr.findElement(By.xpath(//input[@name='regno'])。

  • 我有一个关于科特林的问题: 在kotlin中是否可以用内联和具体化避免这种每个图形类型都有一个接口的方法爆炸?我在努力,但我做不到。 一方面,kotlin接口(我认为)不允许内联函数,另一方面,即使没有该接口,我也不能自动强制转换参数T,将其具体化为工厂类中的特定子类型类之一。