Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

218
Visualizações
How does Java know which overloaded method to call with lambda expressions? (Supplier, Consumer, Callable, ...)

First off, I have no idea how to decently phrase the question, so this is up for suggestions.

Lets say we have following overloaded methods:

void execute(Callable<Void> callable) {
    try {
        callable.call();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

<T> T execute(Supplier<T> supplier) {
    return supplier.get();
}

void execute(Runnable runnable) {
    runnable.run();
}

Going off from this table, I got from another SO question

Supplier       ()    -> x
Consumer       x     -> ()
BiConsumer     x, y  -> ()
Callable       ()    -> x throws ex
Runnable       ()    -> ()
Function       x     -> y
BiFunction     x,y   -> z
Predicate      x     -> boolean
UnaryOperator  x1    -> x2
BinaryOperator x1,x2 -> x3

These are the results I get locally:

// Runnable -> expected as this is a plain void  
execute(() -> System.out.println()); 

// Callable -> why is it not a Supplier? It does not throw any exceptions..
execute(() -> null);

// Supplier -> this returns an Object, but how is that different from returning null?
execute(() -> new Object());

// Callable -> because it can throw an exception, right?
execute(() -> {throw new Exception();});

How does the compiler know which method to call? How does it for example make the distinction between what's a Callable and what's a Runnable?

over 4 years ago · Santiago Trujillo
2 Respostas
Responde à pergunta

0

It all makes sense and has a simple pattern besides () -> null being a Callable I think. The Runnable is clearly different from the Supplier/Callable as it has no input and output values. The difference between Callable and Supplier is that with the Callable you have to handle exceptions.

The reason that () -> null is a Callable without an exception is the return type of your definition Callable<Void>. It requires you to return the reference to some object. The only possible reference to return for Void is null. This means that the lambda () -> null is exactly what your definition demands. It would also work for your Supplier example if you would remove the Callable definition. However, it uses Callable<Void> over Supplier<T> as the Callable has the exact type.

Callable is chosen over Supplier as it is more specific (as a comment already suggested). The Java Docs state that it chooses the most specific type if possible:

Type inference is a Java compiler's ability to look at each method invocation and corresponding declaration to determine the type argument (or arguments) that make the invocation applicable. The inference algorithm determines the types of the arguments and, if available, the type that the result is being assigned, or returned. Finally, the inference algorithm tries to find the most specific type that works with all of the arguments.

over 4 years ago · Santiago Trujillo Relatório

0

I believe I have found where this is described in official documentation, although a bit hard to read.

Here is mentioned:

15.27.3. Type of a Lambda Expression

Note that while boxing is not allowed in a strict invocation context, boxing of lambda result expressions is always allowed - that is, the result expression appears in an assignment context, regardless of the context enclosing the lambda expression. However, if an explicitly typed lambda expression is an argument to an overloaded method, a method signature that avoids boxing or unboxing the lambda result is preferred by the most specific check (§15.12.2.5).

and then here (15.12.2.5) is described analytically how the most specific method is chosen.

So according to this for example as described

One applicable method m1 is more specific than another applicable method m2, for an invocation with argument expressions e1, ..., ek, if any of the following are true:

m2 is generic, and m1 is inferred to be more specific than m2 for argument expressions e1, ..., ek

So

// Callable -> why is it not a Supplier?
execute(() -> null);   <-- Callable shall be picked from 2 options as M2 is generic and M1 is inferred to be more specific

void execute(Callable<Void> callable) {  // <------ M1 
   try {
    callable.call();
  } catch (Exception e) {
      e.printStackTrace();
  }
}


 <T> T execute(Supplier<T> supplier) {  // <------ M2 is Generic
    return supplier.get();
 }

Why M1 is inferred to be more specific can be traced down from this process described here (18.5.4 More Specific Method Inference)

over 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda