Tengo un método que tiene aproximadamente diez líneas de código. Quiero crear más métodos que hagan exactamente lo mismo, excepto por un pequeño cálculo que cambiará una línea de código. Esta es una aplicación perfecta para pasar un puntero de función para reemplazar esa línea, pero Java no tiene punteros de función. ¿Cuál es mi mejor alternativa?
java
closures
function-pointers
Bill el lagarto
fuente
fuente
::
operador, por otro lado ...Respuestas:
Clase interna anónima
Digamos que desea que se pase una función con un
String
parámetro que devuelve unint
.Primero debe definir una interfaz con la función como su único miembro, si no puede reutilizar una existente.
Un método que toma el puntero solo aceptaría una
StringFunction
instancia como esta:Y se llamaría así:
EDITAR: en Java 8, podría llamarlo con una expresión lambda:
fuente
Para cada "puntero de función", crearía una pequeña clase functor que implementa su cálculo. Defina una interfaz que implementarán todas las clases y pase instancias de esos objetos a su función más grande. Esta es una combinación del " patrón de comando " y el " patrón de estrategia ".
El ejemplo de @ sblundy es bueno.
fuente
Cuando hay un número predefinido de cálculos diferentes que puede hacer en esa línea, usar una enumeración es una forma rápida pero clara de implementar un patrón de estrategia.
Obviamente, la declaración del método de estrategia, así como exactamente una instancia de cada implementación, se definen en una sola clase / archivo.
fuente
Debe crear una interfaz que proporcione las funciones que desea transmitir. p.ej:
Luego, cuando necesite pasar una función, puede implementar esa interfaz:
Finalmente, la función de mapa usa la pasada en Function1 de la siguiente manera:
A menudo puede usar Runnable en lugar de su propia interfaz si no necesita pasar parámetros, o puede usar varias otras técnicas para hacer que el parámetro cuente menos "fijo", pero generalmente es una compensación con la seguridad de tipo. (O puede anular el constructor para que su objeto de función pase en los parámetros de esa manera ... hay muchos enfoques, y algunos funcionan mejor en ciertas circunstancias).
fuente
Referencias de métodos utilizando el
::
operadorPuede usar referencias de métodos en argumentos de métodos donde el método acepta una interfaz funcional . Una interfaz funcional es cualquier interfaz que contiene un solo método abstracto. (Una interfaz funcional puede contener uno o más métodos predeterminados o métodos estáticos).
IntBinaryOperator
Es una interfaz funcional. Su método abstractoapplyAsInt
, acepta dosint
s como sus parámetros y devuelve unint
.Math.max
también acepta dosint
sy devuelve unint
. En este ejemplo,A.method(Math::max);
haceparameter.applyAsInt
enviar sus dos valores de entrada ayMath.max
devolver el resultado de esoMath.max
.En general, puedes usar:
en vez de:
que es la abreviatura de:
Para obtener más información, consulte el operador :: (dos puntos) en Java 8 y la Especificación del lenguaje Java §15.13 .
fuente
También puede hacer esto (que en algunas ocasiones RARAS tiene sentido). El problema (y es un gran problema) es que pierde toda la seguridad de tipo de uso de una clase / interfaz y tiene que lidiar con el caso donde el método no existe.
Tiene el "beneficio" de que puede ignorar las restricciones de acceso y llamar a métodos privados (no se muestra en el ejemplo, pero puede llamar a métodos que el compilador normalmente no le permitiría llamar).
De nuevo, es raro que esto tenga sentido, pero en esas ocasiones es una buena herramienta.
fuente
Si tiene solo una línea que es diferente, puede agregar un parámetro como una marca y una instrucción if (marca) que llame a una línea u otra.
fuente
También puede interesarle saber sobre el trabajo en curso para Java 7 que involucra cierres:
¿Cuál es el estado actual de los cierres en Java?
http://gafter.blogspot.com/2006/08/closures-for-java.html
http://tech.puredanger.com/java7/#closures
fuente
Nuevas interfaces funcionales de Java 8 y referencias de métodos utilizando el
::
operador.Java 8 puede mantener referencias de métodos (MyClass :: new) con punteros " @ Functional Interface ". No hay necesidad del mismo nombre de método, solo se requiere la misma firma de método.
Ejemplo:
Entonces, ¿qué tenemos aquí?
¡DEBE UTILIZAR INTERFACES FUNCIONALES PARA LOS ESCUCHADORES SOLO Y SOLO PARA ESO!
Debido a que todos los demás punteros de función son realmente malos para la legibilidad del código y para la capacidad de comprender. Sin embargo, las referencias directas de métodos a veces son útiles, con foreach, por ejemplo.
Existen varias interfaces funcionales predefinidas:
Para versiones anteriores de Java, debe probar Guava Libraries, que tiene una funcionalidad y una sintaxis similares, como Adrian Petrescu ha mencionado anteriormente.
Para una investigación adicional, consulte Java 8 Cheatsheet
y gracias a The Guy with The Hat por el enlace §15.13 de la especificación del lenguaje Java .
fuente
La respuesta de @ sblundy es excelente, pero las clases internas anónimas tienen dos pequeños defectos, el principal es que tienden a no ser reutilizables y el secundario es una sintaxis voluminosa.
Lo bueno es que su patrón se expande en clases completas sin ningún cambio en la clase principal (la que realiza los cálculos).
Cuando crea una instancia de una nueva clase, puede pasar parámetros a esa clase que pueden actuar como constantes en su ecuación, por lo que si una de sus clases internas se ve así:
pero a veces necesitas uno que sea:
y tal vez un tercio que es:
en lugar de crear dos clases internas anónimas o agregar un parámetro "paso a través", puede crear una sola clase ACTUAL que cree como:
Simplemente almacenaría la constante en la clase y la usaría en el método especificado en la interfaz.
De hecho, si SABE que su función no se almacenará / reutilizará, puede hacer esto:
Pero las clases inmutables son más seguras: no puedo encontrar una justificación para hacer que una clase como esta sea mutable.
Realmente solo publico esto porque me avergüenzo cada vez que escucho una clase interna anónima: he visto un montón de código redundante que era "obligatorio" porque lo primero que hizo el programador fue permanecer en el anonimato cuando debería haber usado una clase real y nunca repensó su decisión.
fuente
Las bibliotecas de Google Guava , que se están volviendo muy populares, tienen un objeto genérico Function y Predicate que han trabajado en muchas partes de su API.
fuente
Suena como un patrón de estrategia para mí. Echa un vistazo a los patrones de Java fluffycat.com.
fuente
Una de las cosas que realmente extraño cuando programo en Java son las devoluciones de llamada de funciones. Una situación en la que la necesidad de estos seguía presentándose era en el procesamiento recursivo de las jerarquías donde desea realizar alguna acción específica para cada elemento. Como recorrer un árbol de directorios o procesar una estructura de datos. El minimalista dentro de mí odia tener que definir una interfaz y luego una implementación para cada caso específico.
Un día me pregunté por qué no. Tenemos punteros de método: el objeto Método. Con la optimización de los compiladores JIT, la invocación reflexiva ya no conlleva una gran penalización de rendimiento. Y además de, digamos, copiar un archivo de una ubicación a otra, el costo de la invocación del método reflejado es insignificante.
Mientras lo pensaba más, me di cuenta de que una devolución de llamada en el paradigma OOP requiere unir un objeto y un método: ingrese el objeto Callback.
Echa un vistazo a mi solución basada en la reflexión para Callbacks en Java . Gratis para cualquier uso.
fuente
Bien, este hilo ya es lo suficientemente viejo, así que muy probablemente mi respuesta no sea útil para la pregunta. Pero dado que este hilo me ayudó a encontrar mi solución, lo pondré aquí de todos modos.
Necesitaba usar un método estático variable con entrada y salida conocidas (ambas dobles ). Entonces, conociendo el paquete y el nombre del método, podría trabajar de la siguiente manera:
para una función que acepta un doble como parámetro.
Entonces, en mi situación concreta, lo inicialicé con
e invocado más tarde en una situación más compleja con
donde la actividad es un valor doble arbitrario. Estoy pensando en hacer esto un poco más abstracto y generalizarlo, como lo ha hecho SoftwareMonkey, pero actualmente estoy bastante contento con la forma en que es. Tres líneas de código, sin clases e interfaces necesarias, eso no es tan malo.
fuente
code
descuento, estaba demasiado impaciente y estúpido para encontrarlo ;-)Para hacer lo mismo sin interfaces para una variedad de funciones:
fuente
Echa un vistazo a lambdaj
http://code.google.com/p/lambdaj/
y en particular su nueva característica de cierre
http://code.google.com/p/lambdaj/wiki/Closures
y encontrará una forma muy legible de definir el puntero de cierre o función sin crear una interfaz sin sentido o usar clases internas feas
fuente
Wow, ¿por qué no simplemente crear una clase Delegate que no es tan difícil dado que ya lo hice para Java y usarla para pasar el parámetro donde T es el tipo de retorno? Lo siento, pero como programador de C ++ / C # en general solo estoy aprendiendo Java, necesito punteros de función porque son muy útiles. Si está familiarizado con alguna clase que se ocupe de la información del método, puede hacerlo. En las bibliotecas de Java eso sería java.lang.reflect.method.
Si siempre usa una interfaz, siempre tiene que implementarla. En el manejo de eventos, realmente no hay una mejor manera de registrar / anular el registro de la lista de manejadores, sino para los delegados a los que necesita pasar funciones y no el tipo de valor, lo que hace que una clase de delegado lo maneje para superar una interfaz.
fuente
Ninguna de las respuestas de Java 8 ha dado un ejemplo completo y coherente, así que aquí viene.
Declare el método que acepta el "puntero de función" de la siguiente manera:
Llámalo proporcionando la función con una expresión lambda:
fuente
Si alguien está luchando por pasar una función que requiere un conjunto de parámetros para definir su comportamiento, pero otro conjunto de parámetros sobre los cuales ejecutar, como Scheme's:
ver Función de paso con comportamiento definido por parámetro en Java
fuente
Desde Java8, puede usar lambdas, que también tienen bibliotecas en la API oficial de SE 8.
Uso: debe utilizar una interfaz con un solo método abstracto. Haga una instancia de él (es posible que desee usar el java SE 8 ya proporcionado) de esta manera:
Para obtener más información, consulte la documentación: https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
fuente
Antes de Java 8, el sustituto más cercano para la funcionalidad tipo puntero de función era una clase anónima. Por ejemplo:
Pero ahora en Java 8 tenemos una alternativa muy clara conocida como expresión lambda , que se puede usar como:
donde isBiggerThan es un método en
CustomClass
. También podemos usar referencias de métodos aquí:fuente