una métodos metodos metodo los llamar las estáticos estatico ejemplos definicion declarados como clase almacenan java foreach java-8 method-reference

métodos - Limitaciones de forEach con referencias de método de instancia en Java 8



metodo static java (3)

Eche un vistazo a la sección Referencias de métodos en el Tutorial de Java . Allí dice:

Hay cuatro tipos de referencias de métodos:

  • Referencia a un método estático: ContainingClass::staticMethodName

  • Referencia a un método de instancia de un objeto particular: containingObject::instanceMethodName

  • Referencia a un método de instancia de un objeto arbitrario de un tipo particular: ContainingType::methodName

  • Referencia a un constructor: ClassName::new

Allí se explica que, por ejemplo, TemperatureObserver::react sería una referencia de método del tercer tipo: una referencia a un método de instancia de un objeto arbitrario de un tipo particular. En el contexto de su llamada al método Stream.forEach , esa referencia de método sería equivalente a la siguiente expresión lambda:

(TemperatureObserver item) -> item.react()

O solo:

item -> item.react()

Que no coincide con la firma del método void TemperatureObserver.react(BigDecimal t) .

Como ya sospecha, hay casos en los que no puede encontrar una referencia de método equivalente para un lambda. Las Lambdas son mucho más flexibles, aunque en mi humilde opinión a veces son menos legibles que las referencias a métodos (pero esto es una cuestión de gustos, muchas personas piensan al revés).

Una forma de seguir usando una referencia de método sería con un método auxiliar:

public static <T, U> Consumer<? super T> consumingParam( BiConsumer<? super T, ? super U> biConsumer, U param) { return t -> biConsumer.accept(t, param); }

Que puedes usar de la siguiente manera:

observers.forEach(consumingParam(TemperatureObserver::react, temp));

Pero, honestamente, prefiero usar un lambda.

Supongamos que tengo la siguiente interfaz funcional:

public interface TemperatureObserver { void react(BigDecimal t); }

y luego, en otra clase, una lista de ArrayList ya completada de objetos de tipo TemperatureObserver . Suponiendo que temp es un BigDecimal , puedo invocar react en un bucle usando:

observers.forEach(item -> item.react(temp));

Mi pregunta : ¿Puedo usar una referencia de método para el código anterior?

Lo siguiente no funciona:

observers.forEach(TemperatureObserver::react);

El mensaje de error me dice que

  1. forEach en los Arraylist observers no es aplicable al tipo TemperatureObserver::react
  2. TemperatureObserver no define un método de react(TemperatureObserver)

Justo lo suficiente, como forEach espera como argumento un Consumer<? super TemperatureObserver> Consumer<? super TemperatureObserver> , y mi interfaz, aunque funcional, no cumple con el Consumer debido a los diferentes argumentos de react (un BigDecimal en mi caso).

Entonces, ¿se puede resolver esto o es un caso en el que un lambda no tiene una referencia de método correspondiente?


Hay tres tipos de referencias de métodos que se pueden usar cuando un solo valor está disponible en el flujo:

  1. Un método sin parámetros del objeto transmitido.

    class Observer { public void act() { // code here } } observers.forEach(Observer::act); observers.forEach(obs -> obs.act()); // equivalent lambda

    El objeto transmitido se convierte en el objeto de este método.

  2. Un método estático con el objeto transmitido como parámetro.

    class Other { public static void act(Observer o) { // code here } } observers.forEach(Other::act); observers.forEach(obs -> Other.act(obs)); // equivalent lambda

  3. Un método no estático con el objeto transmitido como parámetro.

    class Other { void act(Observer o); } Other other = new Other(); observers.forEach(other::act); observers.forEach(obs -> other.act(obs)); // equivalent lambda

También hay una referencia del constructor, pero eso no es realmente relevante para esta pregunta.

Como tiene una temp valor externa y desea usar una referencia de método, puede hacer la tercera opción:

class Temp { private final BigDecimal temp; public Temp(BigDecimal temp) { this.temp = temp; } public void apply(TemperatureObserver observer) { observer.react(this.temp); } } Temp tempObj = new Temp(temp); observers.forEach(tempObj::apply);


No funciona, porque se itera sobre los manejadores, no sobre los parámetros.

Por ejemplo, este código funciona:

ArrayList<BigDecimal> temps = new ArrayList<>(); TemperatureObserver observer = new TemperatureObserverImpl(); temps.forEach(observer::react);