2025-02-11 07:00:05

Java代表?

Java语言是否具有委托特性,类似于c#对委托的支持?


当前回答

不,但它内部有相似的行为。

在c#中,委托用于创建一个单独的入口点,它们的工作原理很像函数指针。

在java中没有函数指针,但是在内部java需要做同样的事情来实现这些目标。

例如,在Java中创建线程需要一个类扩展Thread或实现Runnable,因为类对象变量可以用作内存位置指针。

其他回答

它不像c#那样有显式的委托关键字,但你可以在Java 8中通过使用函数接口(即任何只有一个方法的接口)和lambda来实现类似的功能:

private interface SingleFunc {
    void printMe();
}

public static void main(String[] args) {
    SingleFunc sf = () -> {
        System.out.println("Hello, I am a simple single func.");
    };
    SingleFunc sfComplex = () -> {
        System.out.println("Hello, I am a COMPLEX single func.");
    };
    delegate(sf);
    delegate(sfComplex);
}

private static void delegate(SingleFunc f) {
    f.printMe();
}

每个SingleFunc类型的新对象都必须实现printMe(),因此将它传递给另一个方法(例如委托(SingleFunc))来调用printMe()方法是安全的。

是或否,但是Java中的委托模式可以这样考虑。本视频教程讲的是活动片段之间的数据交换,具有使用接口的委托排序模式的精髓。

我已经在Java中使用反射实现了回调/委托支持。详细信息和工作来源可以在我的网站上找到。

工作原理

有一个名为Callback的原理类和一个名为WithParms的嵌套类。需要回调的API将callback对象作为参数,并在必要时创建callback。使用parms作为方法变量。由于这个对象的许多应用程序都是递归的,所以这非常简单。

由于性能仍然是我的首要任务,我不想被要求创建一个一次性对象数组来保存每次调用的参数——毕竟在大型数据结构中可能有数千个元素,而在消息处理场景中,我们可能会在一秒钟内处理数千个数据结构。

In order to be threadsafe the parameter array needs to exist uniquely for each invocation of the API method, and for efficiency the same one should be used for every invocation of the callback; I needed a second object which would be cheap to create in order to bind the callback with a parameter array for invocation. But, in some scenarios, the invoker would already have a the parameter array for other reasons. For these two reasons, the parameter array does not belong in the Callback object. Also the choice of invocation (passing the parameters as an array or as individual objects) belongs in the hands of the API using the callback enabling it to use whichever invocation is best suited to its inner workings.

因此,WithParms嵌套类是可选的,用于两个目的,它包含回调调用所需的参数对象数组,并提供10个重载invoke()方法(具有1到10个参数),这些方法加载参数数组,然后调用回调目标。

下面是一个使用回调处理目录树中的文件的示例。这是一个初始验证过程,只计算要处理的文件,并确保没有超过预定的最大大小。在本例中,我们只是用API调用内联创建回调。但是,我们将目标方法反射为静态值,这样就不会每次都进行反射。

static private final Method             COUNT =Callback.getMethod(Xxx.class,"callback_count",true,File.class,File.class);

...

IoUtil.processDirectory(root,new Callback(this,COUNT),selector);

...

private void callback_count(File dir, File fil) {
    if(fil!=null) {                                                                             // file is null for processing a directory
        fileTotal++;
        if(fil.length()>fileSizeLimit) {
            throw new Abort("Failed","File size exceeds maximum of "+TextUtil.formatNumber(fileSizeLimit)+" bytes: "+fil);
            }
        }
    progress("Counting",dir,fileTotal);
    }

IoUtil.processDirectory ():

/**
 * Process a directory using callbacks.  To interrupt, the callback must throw an (unchecked) exception.
 * Subdirectories are processed only if the selector is null or selects the directories, and are done
 * after the files in any given directory.  When the callback is invoked for a directory, the file
 * argument is null;
 * <p>
 * The callback signature is:
 * <pre>    void callback(File dir, File ent);</pre>
 * <p>
 * @return          The number of files processed.
 */
static public int processDirectory(File dir, Callback cbk, FileSelector sel) {
    return _processDirectory(dir,new Callback.WithParms(cbk,2),sel);
    }

static private int _processDirectory(File dir, Callback.WithParms cbk, FileSelector sel) {
    int                                 cnt=0;

    if(!dir.isDirectory()) {
        if(sel==null || sel.accept(dir)) { cbk.invoke(dir.getParent(),dir); cnt++; }
        }
    else {
        cbk.invoke(dir,(Object[])null);

        File[] lst=(sel==null ? dir.listFiles() : dir.listFiles(sel));
        if(lst!=null) {
            for(int xa=0; xa<lst.length; xa++) {
                File ent=lst[xa];
                if(!ent.isDirectory()) {
                    cbk.invoke(dir,ent);
                    lst[xa]=null;
                    cnt++;
                    }
                }
            for(int xa=0; xa<lst.length; xa++) {
                File ent=lst[xa];
                if(ent!=null) { cnt+=_processDirectory(ent,cbk,sel); }
                }
            }
        }
    return cnt;
    }

This example illustrates the beauty of this approach - the application specific logic is abstracted into the callback, and the drudgery of recursively walking a directory tree is tucked nicely away in a completely reusable static utility method. And we don't have to repeatedly pay the price of defining and implementing an interface for every new use. Of course, the argument for an interface is that it is far more explicit about what to implement (it's enforced, not simply documented) - but in practice I have not found it to be a problem to get the callback definition right.

定义和实现一个接口并不是那么糟糕(除非你像我一样分发applet,避免创建额外的类实际上很重要),但是当你在一个类中有多个回调时,这才是真正的亮点。不仅被迫将它们分别推到一个单独的内部类中增加了部署应用程序的开销,而且编程非常乏味,所有的样板代码实际上只是“噪音”。

不,Java没有这个惊人的特性。但是您可以使用观察者模式手动创建它。这里有一个例子: 用java编写c#委托

通过类路径上的安全镜像,你可以得到类似c#委托和事件的东西。

来自项目README的例子:

Java中的委托!

  Delegate.With1Param<String, String> greetingsDelegate = new Delegate.With1Param<>();
  greetingsDelegate.add(str -> "Hello " + str);
  greetingsDelegate.add(str -> "Goodbye " + str);

  DelegateInvocationResult<String> invocationResult = 
  greetingsDelegate.invokeAndAggregateExceptions("Sir");

  invocationResult.getFunctionInvocationResults().forEach(funInvRes -> 

  System.out.println(funInvRes.getResult()));
  //prints: "Hello sir" and "Goodbye Sir"

事件

  //Create a private Delegate. Make sure it is private so only *you* can invoke it.
  private static Delegate.With0Params<String> trimDelegate = new Delegate.With0Params<>();

  //Create a public Event using the delegate you just created.
  public static Event.With0Params<String> trimEvent= new Event.With0Params<>(trimDelegate)

看看这个SO的答案。