Mit einem Parameter ausführbar?

177

Ich benötige ein "Runnable, das einen Parameter akzeptiert", obwohl ich weiß, dass ein solches Runnable nicht wirklich existiert.

Dies kann auf einen grundlegenden Fehler im Design meiner App und / oder auf eine mentale Blockade in meinem müden Gehirn hinweisen. Ich hoffe, hier einige Ratschläge zu finden, wie Sie Folgendes erreichen können, ohne grundlegende OO-Prinzipien zu verletzen:

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

Irgendeine Idee, wie man so etwas wie das oben Genannte erreicht?

uTubeFan
quelle
7
Jetzt können Sie verwenden Consumer<T>.
Alex78191
1
Ich habe die verschiedenen Antworten auf diese Frage gelesen. Es scheint mir seltsam, dass niemand gesagt hat, dass Sie Ihrem Projekt die Runnables hinzufügen können, die Sie benötigen (mit einem, zwei, drei oder mehr Argumenten), indem Sie einfach eine geeignete Schnittstelle hinzufügen. Ich habe hier einen Kommentar für Interessenten erstellt: gist.github.com/jsfan3/3a66e711fd0fd233c5e4c467184adb7a
Francesco Galgani
Dies ist KEIN Duplikat von "Wie kann ich einen Parameter an einen Java-Thread übergeben?". Und die moderne Antwort lautet, wie @ Alex78191 sagt: useConsumer<T>
Epaga

Antworten:

228

Nun, es ist fast 9 Jahre her, seit ich das ursprünglich gepostet habe und um ehrlich zu sein, hat Java seitdem ein paar Verbesserungen vorgenommen. Ich werde meine ursprüngliche Antwort unten belassen, aber die Leute müssen nicht tun, was darin enthalten ist. Vor 9 Jahren hätte ich mich bei der Codeüberprüfung gefragt, warum sie es getan haben, und es vielleicht genehmigt, vielleicht auch nicht. Angesichts der Verfügbarkeit moderner Lambdas ist es unverantwortlich, eine so hoch bewertete Antwort zu haben, in der ein veralteter Ansatz empfohlen wird (der fairerweise zunächst zweifelhaft war ...). Im modernen Java würde diese Codeüberprüfung sofort abgelehnt, und dies wäre der Fall empfohlen:

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

Nach wie vor werden Details wie der sinnvolle Umgang mit diesem Thread dem Leser als Übung überlassen. Aber um es ganz klar auszudrücken: Wenn Sie Angst vor der Verwendung von Lambdas haben, sollten Sie noch mehr Angst vor Multithread-Systemen haben.

Ursprüngliche Antwort, nur weil:

Sie können eine Klasse direkt in der Methode deklarieren

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
quelle
Danke euch allen! Alle vorgeschlagenen Lösungen weisen auf denselben Ansatz hin, aber ich kann nur einen akzeptieren. Ich muss sehr müde sein, nicht selbst darauf kommen zu können. +1 an alle.
uTubeFan
18
Tatsächlich wissen die meisten Leute nicht, dass Sie eine Klasse innerhalb einer Methode deklarieren können. Einige würden es als schlechten Stil betrachten. Ich denke, es ist Geschmackssache. :)
CorsiKa
3
öffentliche Schnittstelle ResultRunnable <T> {public void run (T result); }
Roman M
46

Sie könnten es in eine Funktion setzen.

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;

}

(Als ich dies verwendete, war mein Parameter eine Ganzzahl-ID, mit der ich eine Hashmap von ID -> myRunnables erstellt habe. Auf diese Weise kann ich die Hashmap verwenden, um verschiedene myRunnable-Objekte in einem Handler zu veröffentlichen / zu entfernen.)

HaoQi Li
quelle
3
Danke, dass du den Code geteilt hast - ich liebe es, wenn Leute das tun, anstatt nur zu plappern. Eine Frage: Ist der obige Ansatz in Bezug auf Speicherverlust in Ordnung? Alle Referenzen, die Sie übergeben, werden ordnungsgemäß entsorgt?
Nikib3ro
2
@ kape123 Die Antwort lautet "es kommt darauf an". Solange ein Runnablevon der Methode zurückgegebenes Objekt irgendwo vorhanden ist, kann paramStres wahrscheinlich nicht zur Speicherbereinigung verwendet werden. Es ist möglich, dass die JIT (oder sogar javac), wenn das Objekt vorhanden ist, aber nie wieder ausgeführt werden kann, beschließt, es aus dem Bereich zu entfernen. Wir sollten uns jedoch nicht auf solche Optimierungen verlassen, da sie sich in Zukunft ändern können.
CorsiKa
"Danke, dass du den Code geteilt hast - ich liebe es, wenn Leute das tun, anstatt nur zu plappern." - was?
Rob 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));

Erstellen Sie eine Init-Funktion, die das Objekt selbst zurückgibt, und initialisieren Sie damit die Parameter.

Tommi Rouvali
quelle
1
Leider können Sie es keiner Variablen zuweisen und init ()
Andrew Glukhoff
11

Ich verwende die folgende Klasse, die die Runnable- Schnittstelle implementiert . Mit dieser Klasse können Sie einfach neue Threads mit Argumenten erstellen

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
quelle
2
Wie würden Sie diese abstrakte Klasse verwenden, um eine ausführbare Datei mit einem Parameter auszuführen?
EZDsIt
Ich bevorzuge es, einen Konstruktor mit Parametern zu verwenden und public RunnableArg(Object... args) { setArgs(args); } dann die lokale Klasse zu beschreiben: class ActionArg extends RunnableArg { public ActionArg(Object... arg) { super(arg); } @Override public void run() { /* getArgs() and process them */ } } und benutze ihn Thread t = new Thread(new ActionArg( %param_object(s)% )); t.start();
GSD.Aaz
10

Sie haben zwei Möglichkeiten:

  1. Definieren Sie eine benannte Klasse. Übergeben Sie Ihren Parameter an den Konstruktor der benannten Klasse.

  2. Lassen Sie Ihre anonyme Klasse über Ihrem "Parameter" schließen. Stellen Sie sicher, dass Sie es als markieren final.

rlibby
quelle
@ Massa: siehe stackoverflow.com/questions/266806/…
setzamora
6

Seit Java 8 ist die beste Antwort Consumer<T>:

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

Es ist eine der funktionalen Schnittstellen, was bedeutet, dass Sie es als Lambda-Ausdruck bezeichnen können:

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

...

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

...
Epaga
quelle
5

Ich möchte zuerst wissen, was Sie hier erreichen möchten, damit ein Argument an new Runnable () oder run () übergeben werden muss. Der übliche Weg sollte darin bestehen, ein ausführbares Objekt zu haben, das Daten (str) an seine Threads übergibt, indem vor dem Start Mitgliedsvariablen festgelegt werden. Die run () -Methode verwendet dann diese Mitgliedsvariablenwerte, um someFunc () auszuführen.

atlantis
quelle