是否有任何可用的开源工具支持通过参数类型和返回类型集搜索 Java 方法?
例如,假设我正在寻找一种为整数数组生成哈希码的方法。我搜索一个采用 int[] 参数并返回 int 的方法:
int[] -> int
屈服
java.util.Arrays#hashCode(int[])
...
或者我可能想找到一个方法,它接受一个字符串、要替换的字符以及要替换它的字符。于是我找了个匹配的方法:
String, char, char -> String
屈服
java.lang.String#replace(char, char)
...
理想情况下,我想要一个与 Haskell 的 Hoogle 等效的 Java ,支持按类型签名搜索函数。
我希望该工具能够:
我知道许多 IDE 支持搜索采用或返回给定类型的方法,但还没有看到通过参数类型 和 返回类型的组合来缩小搜索范围的工具.
最佳答案
Integer.class = Integer.TYPE)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.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;
}
}
关于java - 如何通过类型签名搜索 Java API 方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4188919/
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
我正在尝试设置一个puppet节点,但rubygems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由rubygems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚
我想了解Ruby方法methods()是如何工作的。我尝试使用“ruby方法”在Google上搜索,但这不是我需要的。我也看过ruby-doc.org,但我没有找到这种方法。你能详细解释一下它是如何工作的或者给我一个链接吗?更新我用methods()方法做了实验,得到了这样的结果:'labrat'代码classFirstdeffirst_instance_mymethodenddefself.first_class_mymethodendendclassSecond使用类#returnsavailablemethodslistforclassandancestorsputsSeco
尝试通过RVM将RubyGems升级到版本1.8.10并出现此错误:$rvmrubygemslatestRemovingoldRubygemsfiles...Installingrubygems-1.8.10forruby-1.9.2-p180...ERROR:Errorrunning'GEM_PATH="/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/ruby-1.9.2-p180@global:/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/rub