Java 标准库中哪些方法的参数数量最多?
注意:变量参数 (Varargs) 应计为数组类型的 1 个参数,而不是无限数量的参数。
原因:我正在尝试设计更好的库,并且我正在考虑禁止使用超过 4 个参数的方法......所以我试图在标准库中找到具有大量参数的方法并研究该方法并考虑是否需要这样定义,以及是否有一个有效的案例有超过 4 个参数。
Java 标准库中哪些方法的参数数量最多?
注意:变量参数 (Varargs) 应计为数组类型的 1 个参数,而不是无限数量的参数。
原因:我正在尝试设计更好的库,并且我正在考虑禁止使用超过 4 个参数的方法......所以我试图在标准库中找到具有大量参数的方法并研究该方法并考虑是否需要这样定义,以及是否有一个有效的案例有超过 4 个参数。
限制自己的公共 API 中的参数数量当然是一个很好的目标,但不应盲目地遵守任意规则,然后应用古怪的变通方法来遵循它们。此外,其他人的代码有时应该只是如何不解决问题的灵感......
也就是说,回答实际问题有点困难。你想要_____吗...
public
还是protected
方法?public
类?public
是否在属于专有 API的类中包含方法?不过,我很好奇。使用一个类扫描所有可见类(+1 !),加载它们(并公然忽略错误),从有效类中获取所有方法并查看它们的参数计数,我可以找到一些结果:
总冠军似乎来自 JavaFX 运行时的一个类,称为com.sun.scenario.effect.impl.sw.sse.SSEPhongLighting_SPOTPeer
. 该方法是一种native
简单调用的方法,它filter
接收多达37 个参数:private static native void com.sun.scenario.effect.impl.sw.sse.SSEPhongLighting_SPOTPeer.filter(int[],int,int,int,int,int,int[],float,float,float,float,int,int,int,float,float[],float,float,float,float,float,float,float,float,float,float,int[],float,float,float,float,int,int,int,float,float,float)
。
但是,该方法是private
and native
,并且在 OpenJDK JavaFX 运行时存储库中甚至找不到该类,因此我假设它是自动生成的。
将整个搜索限制在public
也是public
或protected
(而不是native
)的类和方法仍会导致 JavaFX 类之一。这一次,它在com.sun.prism.impl.VertexBuffer
类中,它有一个名为 的方法addMappedPgram
,有24 个参数: public final void com.sun.prism.impl.VertexBuffer.addMappedPgram(float,float,float,float,float,float,float,float,float,float,float,float,float,float,float,float,float,float,float,float,float,float,float,float)
,并且 repo 也包含该方法的源代码。
这是大多数编码指南会说参数数量太多的方法示例。但是参数是如此“常规”(遵循命名模式,可能与四边形的 4 个角有关),我认为这样的事情仍然是合理的。但是该类仍然不应该被客户端使用,并且必须被视为“专有API”。
省略包中的类,这些类以"sun."
或"com.sun."
将我们带到可能被认为是问题的“正确答案”:该类org.w3c.dom.events.MouseEvent
包含一个名为 的方法initMouseEvent
,该方法仍接收15 个参数: public abstract void org.w3c.dom.events.MouseEvent.initMouseEvent(java.lang.String,boolean,boolean,org.w3c.dom.views.AbstractView,int,int,int,int,int,boolean,boolean,boolean,boolean,short,org.w3c.dom.events.EventTarget)
。这是该方法的 JavaDoc API 文档。
(相关的旁注:到目前为止,我遇到的客户端应该使用的参数数量最多的函数是来自 cuDNN的具有31 个参数的函数......)
作为对评论的回应,我现在还介绍了构造函数。
该类javafx.scene.input.ScrollEvent
有两个带有23 个参数的构造函数,即public javafx.scene.input.ScrollEvent(javafx.event.EventType,double,double,double,double,boolean,boolean,boolean,boolean,boolean,boolean,double,double,double,double,double,double,javafx.scene.input.ScrollEvent$HorizontalTextScrollUnits,double,javafx.scene.input.ScrollEvent$VerticalTextScrollUnits,double,int,javafx.scene.input.PickResult)
和public javafx.scene.input.ScrollEvent(java.lang.Object,javafx.event.EventTarget,javafx.event.EventType,double,double,double,double,boolean,boolean,boolean,boolean,boolean,boolean,double,double,double,double,javafx.scene.input.ScrollEvent$HorizontalTextScrollUnits,double,javafx.scene.input.ScrollEvent$VerticalTextScrollUnits,double,int,javafx.scene.input.PickResult)
。这是后者的 API 文档的链接。
我用于测试的代码 - 这很难看而且很hacky,但我认为应该在此处添加:
(编辑也涵盖了构造函数,以回应评论:)
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.function.Predicate;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class ArgCounting
{
static class Entry
{
Class<?> clazz;
Executable executable;
int numParams;
}
public static void main(String[] args) throws Exception
{
List<Entry> entries = new ArrayList<Entry>();
ClassFinder.findClasses(new Visitor<String>()
{
@Override
public boolean visit(String clazz)
{
try
{
System.out.println(clazz);
Class<?> c = Class.forName(clazz);
Method[] methods = c.getDeclaredMethods();
for (Method method : methods)
{
Entry entry = new Entry();
entry.clazz = c;
entry.executable = method;
entry.numParams = method.getParameterCount();
entries.add(entry);
}
Constructor<?>[] constructors = c.getDeclaredConstructors();
for (Constructor<?> constructor : constructors)
{
Entry entry = new Entry();
entry.clazz = c;
entry.executable = constructor;
entry.numParams = constructor.getParameterCount();
entries.add(entry);
}
}
catch (Throwable e)
{
System.out.println("Ignoring: " + e);
}
return true;
}
});
System.out.println("There are " + entries.size() + " executables");
Predicate<Entry> executableIsNotNative =
e -> !Modifier.isNative(e.executable.getModifiers());
Predicate<Entry> executableIsPublic =
e -> Modifier.isPublic(e.executable.getModifiers());
Predicate<Entry> executableIsProtected =
e -> Modifier.isProtected(e.executable.getModifiers());
Predicate<Entry> classIsPublic =
e -> Modifier.isPublic(e.clazz.getModifiers());
List<String> skippedPackagePrefixes = Arrays.asList(
"sun.", "com.sun.");
Predicate<Entry> isSkipped = e ->
{
for (String prefix : skippedPackagePrefixes)
{
Package p = e.clazz.getPackage();
if (p != null)
{
if (p.getName().startsWith(prefix))
{
return true;
}
}
}
return false;
};
Predicate<Entry> isNotSkipped = isSkipped.negate();
Predicate<Entry> executableIsRelevant =
executableIsNotNative.and(executableIsPublic.or(executableIsProtected));
System.out.println("Methods:");
printAllMax(entries,
classIsPublic.and(executableIsRelevant).and(isNotSkipped).and(e -> e.executable instanceof Method));
System.out.println("Constructors:");
printAllMax(entries,
classIsPublic.and(executableIsRelevant).and(isNotSkipped).and(e -> e.executable instanceof Constructor));
}
private static void printAllMax(Collection<Entry> entries, Predicate<Entry> filter)
{
int max = entries.stream()
.filter(filter)
.mapToInt(e -> e.numParams)
.max()
.getAsInt();
System.out.println("Having " + max + " parameters:");
entries.stream().filter(filter.and(e -> e.numParams == max)).forEach(e ->
{
System.out.println(e.executable);
});
}
}
// From https://stackoverflow.com/a/19554704/3182664
interface Visitor<T>
{
/**
* @return {@code true} if the algorithm should visit more results,
* {@code false} if it should terminate now.
*/
public boolean visit(T t);
}
// From https://stackoverflow.com/a/19554704/3182664
class ClassFinder
{
public static void findClasses(Visitor<String> visitor)
{
String classpath = System.getProperty("java.class.path");
String[] paths = classpath.split(System.getProperty("path.separator"));
String javaHome = System.getProperty("java.home");
File file = new File(javaHome + File.separator + "lib");
if (file.exists())
{
findClasses(file, file, true, visitor);
}
for (String path : paths)
{
file = new File(path);
if (file.exists())
{
findClasses(file, file, false, visitor);
}
}
}
private static boolean findClasses(File root, File file,
boolean includeJars, Visitor<String> visitor)
{
if (file.isDirectory())
{
for (File child : file.listFiles())
{
if (!findClasses(root, child, includeJars, visitor))
{
return false;
}
}
}
else
{
if (file.getName().toLowerCase().endsWith(".jar") && includeJars)
{
JarFile jar = null;
try
{
jar = new JarFile(file);
}
catch (Exception ex)
{
}
if (jar != null)
{
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements())
{
JarEntry entry = entries.nextElement();
String name = entry.getName();
int extIndex = name.lastIndexOf(".class");
if (extIndex > 0)
{
if (!visitor.visit(
name.substring(0, extIndex).replace("/", ".")))
{
return false;
}
}
}
}
}
else if (file.getName().toLowerCase().endsWith(".class"))
{
if (!visitor.visit(createClassName(root, file)))
{
return false;
}
}
}
return true;
}
private static String createClassName(File root, File file)
{
StringBuffer sb = new StringBuffer();
String fileName = file.getName();
sb.append(fileName.substring(0, fileName.lastIndexOf(".class")));
file = file.getParentFile();
while (file != null && !file.equals(root))
{
sb.insert(0, '.').insert(0, file.getName());
file = file.getParentFile();
}
return sb.toString();
}
}
(注意:ClassFinder
来自https://stackoverflow.com/a/19554704/3182664!)
对于所提出的实际问题,我没有答案,但我确实认为我对您试图回答的潜在问题有一些有用的见解。
我会在这里参考米勒定律,该定律指出,普通人一次可以在脑海中保留大约 7 件东西(请注意北美本地电话号码是 7 位数字)。
这意味着,我会说,一旦你看到大约 7 个东西,你应该考虑将它们分解并使用组合。例如:
等等
之后,您可以考虑:
这是灵活的(例如,法律规定它实际上是 + 或 - 2),但我认为它可以作为一个有用的基线。