简体   繁体   中英

Bypassing 'Cannot refer to a non-final variable inside an inner class defined'

I have the following code, but when I declare the String line outside of the new Thread class I get an exception. I'm from a C# background, so I understand now that Java does not support true closures. So my question is:

How can I declare a string outside of new Thread and use it in new Thread?

Cannot refer to a non-final variable line inside an inner class defined in a different method

ProcessBuilder builder =
    new ProcessBuilder("/Users/Joe/Desktop/file", "-i", src);
builder.redirectErrorStream(true);
Process process = builder.start();
final InputStream is = process.getInputStream();
String line;
new Thread(new Runnable() {
    @Override public void run() {
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            while ((br.readLine()) != null) {
                line += br.readLine() + "\n";
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }               
}).start();

Change line to be a StringBuilder and declare it as final . Then change the run() method to append to the StringBuilder .

Provided that you join() the child thread before you attempt to read line in the main thread, you don't need to use a StringBuffer .

You have to:

  1. create a new class dedicated to the reading of the Process output (implements Future )
  2. waiting to the instance of its class (a job ) before using it's result with ExecutorService methods.

Here is a code sample:

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class FutureJob {

   static class StreamToString implements Callable< String > {

      BufferedReader br;

      public StreamToString( InputStream in ) {
         br = new BufferedReader( new InputStreamReader( in ));
      }

      @Override public String call() throws Exception {
         StringBuilder buffer = new StringBuilder( 1024 );
         String line;
         while(( line = br.readLine()) != null ) {
            buffer.append( line );
            buffer.append( '\n' );
         }
         br.close();
         return buffer.toString();
      }
   }

   public static void main( String[] args ) throws Exception {
      ProcessBuilder   builder  = new ProcessBuilder( "cmd", "/C", "dir", "c:\\" );
      Process          process  = builder.start();
      ExecutorService  executor = Executors.newSingleThreadExecutor();
      StreamToString   toString = new StreamToString( process.getInputStream());
      Future< String > result   = executor.submit( toString );
      System.out.println( result.get());
      executor.shutdownNow();
   }
}

The blocking wait of the caller is made internally by result.get() , if necessary.

You should create another object which will hold and change the String . Make this object final and change the string using this object method.

You can try using StringBuilder as the final object and use its append function to modify the string.

Alternately, declare line as an instance variable inside the inner class, since you are not using it outside of the class anyway:

new Thread(new Runnable() {
    String line;
    @Override public void run() {
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            while ((br.readLine()) != null) {
                line += br.readLine() + "\n";
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }               
}).start();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM