Which methods are with biggest number of arguments in the Java standard library?
Note: variable Arguments (Varargs) should be counted as 1 argument of type array instead of infinite number of arguments.
Reason: I'm trying to design better Libraries and I'm thinking of banning methods with more than 4 arguments maybe ... So I'm trying to find methods in the standard library that have a large number of arguments and study the method and think of if it was needed to be defined like that and if there is a valid case to have more than 4 arguments.
The goal of limiting the number of parameters in own, public APIs is certainly a good one, but one should not blindly obey arbitrary rules and then apply quirky workarounds to follow them. Also, other people's code should sometimes only be an inspiration of how to not solve something...
That said, answering the actual question is a bit difficult. Do you want to...
public
or protected
methods?public
classes? public
, but proprietary API?However, I was curious. Using a class to scan for all visible classes (+1 there!), loading them (and blatantly ignoring errors), obtaining all methods from the valid classes and having a look at their parameter count, I could find some results:
The overall winner seems to be from a class from the JavaFX runtime, called com.sun.scenario.effect.impl.sw.sse.SSEPhongLighting_SPOTPeer
. The method is a native
method that is simply called filter
, and receives a whopping 37 parameters: 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)
.
However, the method is private
and native
, and the class cannot even be found in the OpenJDK JavaFX runtime repo, so I assume that it is auto-generated somehow.
Limiting the whole search to public
classes and methods that are also public
or protected
(and not native
) still leads to one of the JavaFX classes. This time, it's in the com.sun.prism.impl.VertexBuffer
class, which has a method called addMappedPgram
, with 24 parameters: 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)
, and the repo also contains the source code of this method.
This is an example of a method where most coding guidelines would say that the number of parameters is far too high. But the parameters are so "regular" (following a naming pattern, probably related to 4 corners of a quad) that I think that something like this can still be reasonable. But the class is still not supposed to be used by clients, and has to be considered as "proprietary API".
Omitting classes in packages that start with "sun."
or "com.sun."
brings us to what probably can be considered "the correct answer" to the question: The class org.w3c.dom.events.MouseEvent
contains a method called initMouseEvent
, which still receives 15 parameters: 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)
. And here is the JavaDoc API documentation of that method.
(A related side note: The function with the largest number of parameters that was supposed to be used by clients that I have encountered so far is a function from cuDNN with 31 parameters...)
In response to the comments, I now also covered constructors.
The class javafx.scene.input.ScrollEvent
has two constructors with 23 parameters, namely 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)
and 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)
. Here is the link to the API documentation for the latter.
The code that I used for my tests - this is ugly and hacky, but I think it should be added here:
(Edited to also cover constructors, in response to the comment:)
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();
}
}
(Note: The ClassFinder
is from https://stackoverflow.com/a/19554704/3182664 !)