How Many Type Parameters Can a Java Method Have?

I recently added an interface to my fork of QuickTheories:

public interface QuadFunction<A, B, C, D, E> {
    E apply(A a, B b, C c, D d);

and this made me wonder how many type parameters a method could have. As far as I can tell, the Java Language specification says nothing about this question.1

I had two guesses as to what the implementation defined limit might be:

  1. The compiler would impose a predictable limit like 255 or 65535.
  2. The emergent behavior of the compiler would impose an unexpected limit as a result of implementation details (a stack overflow or something equally unpredictable/irrelevant).

I didn't feel like testing my minimal C++ skills against the source code, so I decided to just test what the compiler did.2 I wrote a Python script that uses binary search to find the fewest type parameters that will cause an error. The complete script is included in a Github repo.

Generating a method is straightforward. Fortunately, we don't have to use any of our type parameters, just emit them in the form <A, B, C...>:

def write_type_plain(count):
    with open('', 'w') as f:
        f.write("public class Test {\n")
        f.write("public <")
        for i in range(count):
            if (i > 0):
                f.write(", ")
            f.write("A" + str(i + 1))
        f.write("> void testMethod() {}")

Running the binary search gives us the following output:

>>> error: UTF8 representation for string "<A1:Ljava/lang/Objec..." is too long for the constant pool
>>> largest type: 2776

This error is a bit obscure, but predictable with hindsight. The class file generated by the compiler contains a number of strings, including the method signatures for each of the methods in the class. These strings are stored in the constant pool, and the entries in the constant pool have a maximum size of 65535 bytes, a limit which is imposed by the JVM spec.

So, neither of my earlier guesses was precisely right. The maximum number of type parameters is an emergent property, rather than an explicit decision. Still, it's not the implementation of the compiler per se that causes the error.3 Instead, the class file format of the JVM limits the number of type parameters you can represent in a class file. This is true in spite of the JVM knowing nothing about generics.

This also means that the maximum number of type parameters depends on exactly how you write your method.4 I tried a new way of encoding type parameters (write_type_compact in the previously linked file), using the full range of legal ASCII characters (A-Z, a-z, $ and _). The implementation is somewhat finicky, because the characters 0-9 can be used, but cannot be the initial character of an identifier, and because Java keywords cannot appear as type parameters. I cheated by only replacing the short words if and do with equal length UTF-8 characters. The more compact encoding increased the number of parameters from 2776 to 3123.

Inconveniently, _A is a legal Java identifier, but _ is not. Thankfully, my encoding produces 3392 2-byte type parameters without using an initial _, so I didn't feel compelled to do the bookkeeping to emit initial _ characters.

One More Trick

Decompiling the classfile shows that the bulk of the 65536 characters isn't the type parameters I generated, but repeated instances of the substring Ljava/lang/Object;. Because no information is given about the type parameters, the classfile reports that they extend object, and encodes that in the method signature. I modified my generator to work around that fact.

The key part of the loop becomes:

s = type_var(i)
if (s != 'A'):
    f.write(" extends A")

and all but one instance of java/lang/Object in the type parameters are replaced by A. After making this change, a method with 9851 type parameters compiled.

Since the number of parameters has increased so much, the encoding I'm using almost certainly needs to be tweaked. Using non-ASCII unicode identifiers is probably necessary to be perfectly efficient, but I'm satisfied with simply pointing out that this could be done.

None of This Matters

It's hard to imagine a practical case where anyone would reach this limit. Code generation can sometimes hit language or compiler limits, but even generated code seems unlikely to use hundreds, much less thousands of type parameters.

Nonetheless, if I was king, I'd consider explicitly banning any class or method from having more than 255 type parameters. An explicit limit seems better, even if it only affects 1 program in a million.

  1. §4.4, §8.1.2, §9.1.2, §8.4.4, §8.8.4 all relate to type parameters for methods or classes, but do not specify how many parameters are allowed.

  2. Since writing this line, I remembered that while Hotspot is C++, javac is written in Java. Had I realized that, I probably still would've experimented, rather than reading source code. Hell is other people's code.

  3. The spaces after the comma do not matter, because the compiler normalizes its output.

  4. This also means that it shouldn't matter which JVM I used. For completeness sake, I used OpenJDK 1.8.0_191-b13 on Fedora 29.