Executável com um parâmetro?

178

Eu preciso de um "Runnable que aceite um parâmetro", embora eu saiba que esse executável não existe realmente.

Isso pode apontar para uma falha fundamental no design do meu aplicativo e / ou um bloqueio mental no meu cérebro cansado, por isso espero encontrar aqui alguns conselhos sobre como realizar algo como o seguinte, sem violar os princípios fundamentais de OO:

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

Alguma idéia de como realizar algo como o acima?

uTubeFan
fonte
7
Agora você pode usar Consumer<T>.
Alex78191
1
Eu li as várias respostas para esta pergunta. Parece-me estranho que ninguém tenha dito que você pode adicionar ao seu projeto os Runnables que você precisa (com um, dois, três ou mais argumentos) simplesmente adicionando uma interface apropriada. Eu criei uma essência comentou aqui para quem está interessado: gist.github.com/jsfan3/3a66e711fd0fd233c5e4c467184adb7a
Francesco Galgani
Isso NÃO é uma duplicata para "Como posso passar um parâmetro para um encadeamento Java". E a resposta moderna é, como @ Alex78191 diz: useConsumer<T>
Epaga

Respostas:

228

Bem, já faz quase 9 anos desde que eu postei isso originalmente e, para ser sincero, o Java fez algumas melhorias desde então. Deixarei minha resposta original abaixo, mas não há necessidade de as pessoas fazerem o que está nele. Há 9 anos, durante a revisão de código, eu teria questionado por que eles fizeram e talvez tenham aprovado, talvez não. Com as lambdas modernas disponíveis, é irresponsável ter uma resposta tão votada recomendando uma abordagem antiquada (que, com toda a justiça, era duvidosa para começar ...) No Java moderno, essa revisão de código seria imediatamente rejeitada, e isso seria sugerido:

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

Como antes, detalhes como lidar com esse tópico de maneira significativa são deixados como um exercício para o leitor. Mas, para ser franco, se você tem medo de usar lambdas, deve ter ainda mais medo de sistemas multiencadeados.

Resposta original, apenas porque:

Você pode declarar uma classe diretamente no 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
fonte
Obrigado a todos! Todas as soluções sugeridas apontam para a mesma abordagem, mas só posso aceitar uma. Eu devo estar muito cansada por não conseguir fazer isso sozinha. +1 para todos.
uTubeFan 2/11/11
18
Na verdade, a maioria das pessoas não sabe que você pode declarar uma classe dentro de um método. Alguns considerariam o estilo pobre. Eu acho que é uma questão de gosto. :)
corsiKa
3
interface pública ResultRunnable <T> {public void run (resultado T); }
Roman M
46

Você poderia colocá-lo em uma função.

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;

}

(Quando eu usei isso, meu parâmetro era um ID inteiro, que eu usei para criar um hashmap de ID -> myRunnables. Dessa forma, eu posso usar o hashmap para postar / remover diferentes objetos myRunnable em um manipulador.)

HaoQi Li
fonte
3
Obrigado por compartilhar o código - adoro quando as pessoas fazem isso em vez de tagarelar. Uma pergunta - a abordagem acima está correta quando se trata de vazamento de memória? Todas as referências aprovadas serão descartadas corretamente?
Nikib3ro
2
@ kape123 A resposta é "depende". Enquanto um Runnableobjeto retornado pelo método existir em qualquer lugar, paramStrprovavelmente não será elegível para a coleta de lixo. É possível que, se o objeto existir, mas nunca puder ser executado novamente, o JIT (ou mesmo o javac) decida removê-lo do escopo, mas não devemos confiar nessas otimizações, pois elas podem mudar no futuro.
corsiKa
"Obrigado por compartilhar o código - eu amo quando as pessoas fazem isso em vez de apenas tagarelar". - que?
Grant
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));

Crie a função init que retorna o próprio objeto e inicialize os parâmetros com ele.

Tommi Rouvali
fonte
1
Infelizmente você não será capaz de atribuí-la a uma variável e chamar init ()
Andrew Glukhoff
11

Eu uso a seguinte classe que implementa a interface Runnable . Com essa classe, você pode criar facilmente novos threads com 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
fonte
2
Como você usaria essa classe abstrata para executar um executável com um parâmetro?
precisa saber é o seguinte
Eu prefiro usar o construtor com parâmetro (s) e public RunnableArg(Object... args) { setArgs(args); } depois descrever a classe local: class ActionArg extends RunnableArg { public ActionArg(Object... arg) { super(arg); } @Override public void run() { /* getArgs() and process them */ } } e usá-lo Thread t = new Thread(new ActionArg( %param_object(s)% )); t.start();
GSD.Aaz
10

Você tem duas opções:

  1. Defina uma classe nomeada. Passe seu parâmetro para o construtor da classe nomeada.

  2. Faça com que sua classe anônima feche seu "parâmetro". Certifique-se de marcar como final.

rlibby
fonte
6

Desde o Java 8, a melhor resposta é usar Consumer<T>:

https://docs.oracle.com/javase/8/docs/api/java/util/function/Consumer.html

É uma das interfaces funcionais, o que significa que você pode chamá-lo como uma expressão lambda:

void doSomething(Consumer<String> something) {
    something.accept("hello!");
}

...

doSomething( (something) -> System.out.println(something) )

...
Epaga
fonte
5

Gostaria primeiro de saber o que você está tentando realizar aqui para precisar que um argumento seja passado para o novo Runnable () ou run (). A maneira usual deve ser ter um objeto Runnable que transmita dados (str) para seus threads definindo variáveis ​​de membros antes de iniciar. O método run () usa esses valores de variável de membro para executar someFunc ()

Atlantis
fonte