Ejecutable con un parámetro?

178

Necesito un "Ejecutable que acepte un parámetro" aunque sé que dicho ejecutable realmente no existe.

Esto puede indicar un defecto fundamental en el diseño de mi aplicación y / o un bloqueo mental en mi cerebro cansado, por lo que espero encontrar aquí algunos consejos sobre cómo lograr algo como lo siguiente, sin violar los principios fundamentales de OO:

  private Runnable mOneShotTask = new Runnable(String str) {
    public void run(String str) {
       someFunc(str);
    }
  };  

¿Alguna idea de cómo lograr algo como lo anterior?

uTubeFan
fuente
77
Ahora puedes usar Consumer<T>.
Alex78191
1
He leído las diferentes respuestas a esta pregunta. Me parece extraño que nadie le haya dicho que puede agregar a su proyecto los Runnables que necesita (con uno, dos, tres o más argumentos) simplemente agregando una interfaz adecuada. Creé
Francesco Galgani
Este NO es un duplicado de "¿Cómo puedo pasar un parámetro a un hilo de Java?". Y la respuesta moderna es, como @ Alex78191 dice: useConsumer<T>
Epaga

Respuestas:

228

Bueno, han pasado casi 9 años desde que publiqué esto originalmente y, para ser honesto, Java ha realizado un par de mejoras desde entonces. Dejaré mi respuesta original a continuación, pero no hay necesidad de que la gente haga lo que contiene. Hace 9 años, durante la revisión del código, habría cuestionado por qué lo hicieron y tal vez lo aprobó, tal vez no. Con las lambdas modernas disponibles, es irresponsable tener una respuesta tan votada que recomiende un enfoque anticuado (que, para ser justos, era dudoso al principio ...) En Java moderno, esa revisión de código sería rechazada de inmediato, y esto sería sugirió:

void foo(final String str) {
    Thread t = new Thread(() -> someFunc(str));
    t.start();
}

Como antes, detalles como manejar ese hilo de manera significativa se dejan como ejercicio para el lector. Pero para decirlo sin rodeos, si tiene miedo de usar lambdas, debería tener aún más miedo de los sistemas de subprocesos múltiples.

Respuesta original, solo porque:

Puedes declarar una clase directamente en el método

void Foo(String str) {
    class OneShotTask implements Runnable {
        String str;
        OneShotTask(String s) { str = s; }
        public void run() {
            someFunc(str);
        }
    }
    Thread t = new Thread(new OneShotTask(str));
    t.start();
}
corsiKa
fuente
¡Gracias a todos! Todas las soluciones sugeridas apuntan al mismo enfoque, pero solo puedo aceptar uno. Debo estar muy cansado de no poder llegar a esto yo mismo. +1 a todos.
uTubeFan
18
En realidad, la mayoría de la gente no sabe que puedes declarar una clase dentro de un método. Algunos lo considerarían un estilo pobre. Supongo que es cuestión de gustos. :)
corsiKa
3
interfaz pública ResultRunnable <T> {ejecución nula pública (resultado T); }
Roman M
46

Podrías ponerlo en una función.

String paramStr = "a parameter";
Runnable myRunnable = createRunnable(paramStr);

private Runnable createRunnable(final String paramStr){

    Runnable aRunnable = new Runnable(){
        public void run(){
            someFunc(paramStr);
        }
    };

    return aRunnable;

}

(Cuando usé esto, mi parámetro era un ID entero, que usé para hacer un hashmap de ID -> myRunnables. De esa manera, puedo usar el hashmap para publicar / eliminar diferentes objetos myRunnable en un controlador).

HaoQi Li
fuente
3
Gracias por compartir el código. Me encanta cuando la gente hace eso en lugar de simplemente parlotear. Una pregunta: ¿está bien el enfoque anterior cuando se trata de pérdida de memoria? ¿Todas las referencias que pases se eliminarán correctamente?
nikib3ro
2
@ kape123 La respuesta es "depende". Mientras exista un Runnableobjeto devuelto por el método en cualquier lugar, paramStrprobablemente no será elegible para la recolección de basura. Es posible que si el objeto existe pero nunca se puede ejecutar de nuevo, el JIT (o incluso javac) puede decidir eliminarlo del alcance, pero no debemos confiar en tales optimizaciones porque pueden cambiar en el futuro.
corsiKa
"Gracias por compartir el código. Me encanta cuando la gente hace eso en lugar de simplemente parlotear". - ¿qué?
Rob Grant el
30
theView.post(new Runnable() {
    String str;
    @Override                            
    public void run() {
        par.Log(str);                              
    }
    public Runnable init(String pstr) {
        this.str=pstr;
        return(this);
    }
}.init(str));

Cree una función de inicio que devuelva el objeto e inicialice los parámetros con él.

Tommi Rouvali
fuente
1
Desafortunadamente no podrá asignarlo a una variable y llamar a init ()
Andrew Glukhoff
11

Yo uso la siguiente clase que implementa la interfaz Runnable . Con esta clase puedes crear fácilmente nuevos hilos con argumentos

public abstract class RunnableArg implements Runnable {

    Object[] m_args;

    public RunnableArg() {
    }

    public void run(Object... args) {
        setArgs(args);
        run();
    }

    public void setArgs(Object... args) {
        m_args = args;
    }

    public int getArgCount() {
        return m_args == null ? 0 : m_args.length;
    }

    public Object[] getArgs() {
        return m_args;
    }
}
ubiquibacon
fuente
2
¿Cómo usarías esta clase abstracta para ejecutar un ejecutable con un parámetro?
EZDs:
Prefiero usar el constructor con parámetro (s) y public RunnableArg(Object... args) { setArgs(args); } luego describir la clase local: class ActionArg extends RunnableArg { public ActionArg(Object... arg) { super(arg); } @Override public void run() { /* getArgs() and process them */ } } y usarla Thread t = new Thread(new ActionArg( %param_object(s)% )); t.start();
GSD.Aaz
10

Tienes dos opciones:

  1. Definir una clase con nombre. Pase su parámetro al constructor de la clase nombrada.

  2. Haga que su clase anónima se cierre sobre su "parámetro". Asegúrese de marcarlo como final.

rlibby
fuente
5

Primero me gustaría saber qué está tratando de lograr aquí para necesitar un argumento que se pase al nuevo Runnable () o al run (). La forma habitual debería ser tener un objeto Runnable que pase datos (str) a sus subprocesos estableciendo variables miembro antes de comenzar. El método run () luego usa estos valores de variables miembro para ejecutar someFunc ()

Atlantis
fuente