在java中将函数作为参数传递

Posted

技术标签:

【中文标题】在java中将函数作为参数传递【英文标题】:Passing function as a parameter in java 【发布时间】:2013-05-23 22:27:14 【问题描述】:

我正在熟悉 android 框架和 Java,并想创建一个通用的“NetworkHelper”类来处理大部分网络代码,使我能够从中调用网页。

我按照 developer.android.com 上的这篇文章创建了我的网络类:http://developer.android.com/training/basics/network-ops/connecting.html

代码:

package com.example.androidapp;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.util.Log;



/**
 * @author tuomas
 * This class provides basic helper functions and features for network communication.
 */


public class NetworkHelper 

private Context mContext;


public NetworkHelper(Context mContext)

    //get context
    this.mContext = mContext;



/**
 * Checks if the network connection is available.
 */
public boolean checkConnection()

    //checks if the network connection exists and works as should be
    ConnectivityManager connMgr = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();

    if (networkInfo != null && networkInfo.isConnected())
    
        //network connection works
        Log.v("log", "Network connection works");
        return true;
    
    else
    
        //network connection won't work
        Log.v("log", "Network connection won't work");
        return false;
    



public void downloadUrl(String stringUrl)

    new DownloadWebpageTask().execute(stringUrl);





//actual code to handle download
private class DownloadWebpageTask extends AsyncTask<String, Void, String>




    @Override
    protected String doInBackground(String... urls)
    
        // params comes from the execute() call: params[0] is the url.
        try 
            return downloadUrl(urls[0]);
         catch (IOException e) 
            return "Unable to retrieve web page. URL may be invalid.";
        
    

    // Given a URL, establishes an HttpUrlConnection and retrieves
    // the web page content as a InputStream, which it returns as
    // a string.
    private String downloadUrl(String myurl) throws IOException 
    
        InputStream is = null;
        // Only display the first 500 characters of the retrieved
        // web page content.
        int len = 500;

        try 
            URL url = new URL(myurl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setReadTimeout(10000 );
            conn.setConnectTimeout(15000);
            conn.setRequestMethod("GET");
            conn.setDoInput(true);
            // Starts the query
            conn.connect();
            int response = conn.getResponseCode();
            Log.d("log", "The response is: " + response);
            is = conn.getInputStream();

            // Convert the InputStream into a string
            String contentAsString = readIt(is, len);
            return contentAsString;

        // Makes sure that the InputStream is closed after the app is
        // finished using it.
         finally 
            if (is != null) 
                is.close();
             
        
    

    // Reads an InputStream and converts it to a String.
    public String readIt(InputStream stream, int len) throws IOException, UnsupportedEncodingException 
    
        Reader reader = null;
        reader = new InputStreamReader(stream, "UTF-8");        
        char[] buffer = new char[len];
        reader.read(buffer);
        return new String(buffer);
    


    // onPostExecute displays the results of the AsyncTask.
    @Override
    protected void onPostExecute(String result) 
    
        //textView.setText(result);
        Log.v("log", result);

    

 

在我的活动课程中,我以这种方式使用课程:

connHelper = new NetworkHelper(this);

...

if (connHelper.checkConnection())
    
        //connection ok, download the webpage from provided url
        connHelper.downloadUrl(stringUrl);
    

我遇到的问题是我应该以某种方式对活动进行回调,并且它应该可以在“downloadUrl()”函数中定义。例如下载完成后,activity中的public void "handleWebpage(String data)" 函数被调用,加载的字符串作为参数。

我做了一些谷歌搜索,发现我应该以某种方式使用接口来实现此功能。在查看了几个类似的 *** 问题/答案后,我没有得到它的工作,我不确定我是否正确理解了接口:How do I pass method as a parameter in Java? 老实说,使用匿名类对我来说是新的,我不确定在哪里或我应该如何在提到的线程中应用示例代码 sn-ps。

所以我的问题是如何将回调函数传递给我的网络类并在下载完成后调用它?接口声明去哪里,实现关键字等等? 请注意,我是 Java 的初学者(虽然有其他编程背景),所以我很感谢您的完整解释:)谢谢!

【问题讨论】:

【参考方案1】:

使用回调接口或带有抽象回调方法的抽象类。

回调接口示例:

public class SampleActivity extends Activity 

    //define callback interface
    interface MyCallbackInterface 

        void onDownloadFinished(String result);
    

    //your method slightly modified to take callback into account 
    public void downloadUrl(String stringUrl, MyCallbackInterface callback) 
        new DownloadWebpageTask(callback).execute(stringUrl);
    

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);

        //example to modified downloadUrl method
        downloadUrl("http://google.com", new MyCallbackInterface() 

            @Override
            public void onDownloadFinished(String result) 
                // Do something when download finished
            
        );
    

    //your async task class
    private class DownloadWebpageTask extends AsyncTask<String, Void, String> 

        final MyCallbackInterface callback;

        DownloadWebpageTask(MyCallbackInterface callback) 
            this.callback = callback;
        

        @Override
        protected void onPostExecute(String result) 
            callback.onDownloadFinished(result);
        

        //except for this leave your code for this class untouched...
    

第二个选项更加简洁。您甚至不必为“onDownloaded 事件”定义抽象方法,因为onPostExecute 完全可以满足您的需要。只需在 downloadUrl 方法中使用匿名内联类扩展您的 DownloadWebpageTask

    //your method slightly modified to take callback into account 
    public void downloadUrl(String stringUrl, final MyCallbackInterface callback) 
        new DownloadWebpageTask() 

            @Override
            protected void onPostExecute(String result) 
                super.onPostExecute(result);
                callback.onDownloadFinished(result);
            
        .execute(stringUrl);
    

    //...

【讨论】:

谢谢,这帮助我解决了问题,我想我现在了解了接口的基础知识:) 看看接口在一般 java 编程中如何发挥重要作用很有趣。【参考方案2】:

无需接口、无需库、无需 Java 8!

只使用来自java.util.concurrentCallable&lt;V&gt;

public static void superMethod(String simpleParam, Callable<Void> methodParam) 

    //your logic code [...]

    //call methodParam
    try 
        methodParam.call();

     catch (Exception e) 
        e.printStackTrace();
    

如何使用:

 superMethod("Hello world", new Callable<Void>() 
                public Void call() 
                    myParamMethod();
                    return null;
                
            
    );

其中myParamMethod() 是我们作为参数传递的方法(在本例中为methodParam)。

【讨论】:

感谢您的回答。但是这个例子并没有说清楚(我整晚都没有睡,所以请原谅我的愚蠢问题)myParamMethod 是如何传递给 simpleParam 的。例如,我有一个围绕 Ion 的包装器,我将服务器参数和包含在 Json 中的目标 URL 传递给它,我去 superMethod(serverParams, callEndpointIon);还是我必须每次都覆盖 Callable? 如果您使用的是Callable&lt;Void&gt;,则没有真正的理由不使用Runnable,因为无论如何您都会返回Void。它将消除对 return null; 声明的需要。 ...并且没有输入参数)) 我如何无法将参数传递给可调用的 methodParam.call(object);并接收 public Void call(JSONObject object) // myParamMethod(JSONObject object);返回空值; 【参考方案3】:

是的,界面是恕我直言的最佳方式。例如,GWT 使用带有如下界面的命令模式:

public interface Command
    void execute();

这样,你可以将函数从一个方法传递给另一个

public void foo(Command cmd)
  ...
  cmd.execute();


public void bar()
  foo(new Command()
     void execute()
        //do something
     
  );

【讨论】:

什么是 GWT 以及如何传递任何参数? @Buksy 是您要找的吗?公共接口命令无效执行(对象...对象); 传递无限的对象:D【参考方案4】:

开箱即用的解决方案是,这在 Java 中是不可能的。 Java 不接受Higher-order functions。它可以通过一些“技巧”来实现。通常,该界面是您所看到的界面。请查看here 了解更多信息。也可以使用反射来实现,但是这样容易出错。

【讨论】:

这并不值得作为一个答案,因为你所做的一切都暗示了如何研究它会更适合作为评论。 因为他的rep不到50,所以不能评论,只能回答。我一直不喜欢那样。 非常有用的概念性答案,对于迁移到 Java 的经验丰富的程序员。谢谢,@olorin!【参考方案5】:

使用接口可能是 Java 编码架构中最好的方法。

但是,传递 Runnable 对象也可以,而且我认为它会更加实用和灵活。

 SomeProcess sp;

 public void initSomeProcess(Runnable callbackProcessOnFailed) 
     final Runnable runOnFailed = callbackProcessOnFailed; 
     sp = new SomeProcess();
     sp.settingSomeVars = someVars;
     sp.setProcessListener = new SomeProcessListener() 
          public void OnDone() 
             Log.d(TAG,"done");
          
          public void OnFailed()
             Log.d(TAG,"failed");
             //call callback if it is set
             if (runOnFailed!=null) 
               Handler h = new Handler();
               h.post(runOnFailed);
             
                         
     ;


/****/

initSomeProcess(new Runnable() 
   @Override
   public void run() 
       /* callback routines here */
   
);

【讨论】:

非常干净整洁的实现。【参考方案6】:

反射从来都不是一个好主意,因为它更难阅读和调试,但如果你 100% 确定你在做什么,你可以简单地调用 set_method(R.id.button_profile_edit, "toggle_edit") 之类的东西来附加一种视图的方法。这在片段中很有用,但同样,有些人会将其视为反模式,因此请注意。

public void set_method(int id, final String a_method)

    set_listener(id, new View.OnClickListener() 
        public void onClick(View v) 
            try 
                Method method = fragment.getClass().getMethod(a_method, null);
                method.invoke(fragment, null);
             catch (Exception e) 
                Debug.log_exception(e, "METHOD");
            
        
    );

public void set_listener(int id, View.OnClickListener listener)

    if (root == null) 
        Debug.log("WARNING fragment", "root is null - listener not set");
        return;
    
    View view = root.findViewById(id);
    view.setOnClickListener(listener);

【讨论】:

以上是关于在java中将函数作为参数传递的主要内容,如果未能解决你的问题,请参考以下文章

如何在用户定义的函数中将数据库列作为参数传递?

在 Java 中将数组作为参数传递时创建数组

是否可以在c中将函数作为参数传递? [复制]

在 C++ 中将数组作为函数参数传递

如何在类中将函数作为参数传递?

如何在 Java 中将类型作为方法参数传递