I created a custom annotation in order to apply currying to a method or constructor.

You can find the complete code for this here: curry.

For more information on currying check this post and for a background on annotations in Java check this one.

First we create the annotation:

@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
public @interface Curry {}

We only need the annotation to be processed and then discarded (we don’t need to have it available at run time or recorded on the .class) so we use @Retention(RetentionPolicy.SOURCE).

This annotation will only be used on methods and constructors so we set those as the Target on @Target({ElementType.METHOD, ElementType.CONSTRUCTOR}).

Then we create a processor for the annotation:

public class CurryProcessor extends AbstractProcessor {

Using @SupportedAnnotationTypes("dev.jsedano.curry.annotation.Curry") we say that this processor will only look for that particular annotation.

@SupportedSourceVersion(SourceVersion.RELEASE_17) here we are saying the Java version supported.

The last one is pretty interesting, @AutoService(Processor.class) is from a Google library called auto-service. In order for the Java compiler to use a processor the class needs to be declared inside the jar on the META-INF/services directory on the javax.annotation.processing.Processor file, the auto-service library does that for you.

Then we need to implement the process method on out custom processor.

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

Inside our class we get the processingEnv object that provides us with functionality such as getMessager() that we use to print a warning message on compile time when the @Curry annotation is used on a method with 1 or more than 10 parameters:

        e ->
                    "incorrect number of parameters, allowed only between 2 and 1O, will not generate code for this one",

We also have this one getFiler() which allows us to create Java source files:

JavaFileObject builderFile = processingEnv.getFiler().createSourceFile(builderClassName);

On the tests module of the project we declare some methods with the @Curry annotation, for example this constructor:

public AnnotatedClass(
    boolean aBoolean, List<String> aStringList, int aNumber, char aChar, float aFloat) {
  this.aBoolean = aBoolean;
  this.aStringList = aStringList;
  this.aNumber = aNumber;
  this.aChar = aChar;
  this.aFloat = aFloat;

After running mvn clean verify on the parent module we can see the autogenerated code under target/generated-sources/annotations/dev.jsedano.curry.tests:

public static java.util.function.Function<java.lang.Boolean,java.util.function.Function<java.util.List<java.lang.String>,java.util.function.Function<java.lang.Integer,java.util.function.Function<java.lang.Character,java.util.function.Function<java.lang.Float,dev.jsedano.curry.tests.AnnotatedClass>>>>> pentaConstructor(dev.jsedano.curry.util.function.PentaFunction<java.lang.Boolean,java.util.List<java.lang.String>,java.lang.Integer,java.lang.Character,java.lang.Float,dev.jsedano.curry.tests.AnnotatedClass> function) {
    return v0->v1->v2->v3->v4-> function.apply(v0,v1,v2,v3,v4);

It is not pretty looking, but we can use it to then curry the five parameter constructor of the example class:

var pentaConstructor = AnnotatedClassCurryer.pentaConstructor(AnnotatedClass::new);

You can see another example here, but if you want to compile it you need to do mvn clean install on the curryer module of curry.

public static String wget(
    int connectionTimeout,
    int readTimeout,
    boolean followRedirects,
    String requestMethod,
    String address) {
  try {
    URL url = new URL(address);
    HttpURLConnection con = (HttpURLConnection) url.openConnection();
    BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
    String inputLine;
    StringBuffer content = new StringBuffer();
    while ((inputLine = in.readLine()) != null) {
    return address + " " + content.toString();
  } catch (Exception e) {
    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw);
    String stackTrace = sw.toString();
    return address + " " + stackTrace.substring(0, stackTrace.indexOf("\n"));

Then we can set the values we need in a curried way and use it:

public static void main(String[] args) {
  var get =


Download the complete code from this post here: curry.