Java代表?

Java Delegates?

Java语言是否具有委托功能,类似于C#对委托的支持?


不是,不是

通过使用反射获取可以随后调用的Method对象,您可能能够实现相同的效果,另一种方法是使用单个"调用"或"执行"方法创建一个接口,然后实例化它们以调用该方法。您感兴趣的(即使用匿名内部类)。

您可能还会发现本文有趣/有用:Java程序员研究C#委托


精确地根据您的意思,您可以使用策略模式实现类似的效果(绕过一种方法)。

而不是像这样的行声明命名方法签名:

1
2
// C#
public delegate void SomeFunction();

声明一个接口:

1
2
3
4
// Java
public interface ISomeBehaviour {
   void SomeFunction();
}

对于该方法的具体实现,请定义一个实现行为的类:

1
2
3
4
5
6
7
8
9
10
11
12
// Java
public class TypeABehaviour implements ISomeBehaviour {
   public void SomeFunction() {
      // TypeA behaviour
   }
}

public class TypeBBehaviour implements ISomeBehaviour {
   public void SomeFunction() {
      // TypeB behaviour
   }
}

然后,无论您在C#中有一个SomeFunction委托,都应改为使用ISomeBehaviour引用:

1
2
3
4
5
6
7
8
9
10
11
// C#
SomeFunction doSomething = SomeMethod;
doSomething();
doSomething = SomeOtherMethod;
doSomething();

// Java
ISomeBehaviour someBehaviour = new TypeABehaviour();
someBehaviour.SomeFunction();
someBehaviour = new TypeBBehaviour();
someBehaviour.SomeFunction();

使用匿名内部类,您甚至可以避免声明单独的命名类,而几乎将它们视为真实的委托函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
// Java
public void SomeMethod(ISomeBehaviour pSomeBehaviour) {
   ...
}

...

SomeMethod(new ISomeBehaviour() {
   @Override
   public void SomeFunction() {
      // your implementation
   }
});

仅当实现非常特定于当前上下文并且不会从重用中受益时,才应该使用此方法。

然后,当然在Java 8中,这些基本上变成了lambda表达式:

1
2
// Java 8
SomeMethod(() -> { /* your implementation */ });


短篇小说:-------------------不。

Introduction

The newest version of the Microsoft Visual J++ development environment
supports a language construct called delegates or bound method
references. This construct, and the new keywords delegate and
multicast introduced to support it, are not a part of the JavaTM
programming language, which is specified by the Java Language
Specification and amended by the Inner Classes Specification included
in the documentation for the JDKTM 1.1 software.

It is unlikely that the Java programming language will ever include
this construct. Sun already carefully considered adopting it in 1996,
to the extent of building and discarding working prototypes. Our
conclusion was that bound method references are unnecessary and
detrimental to the language. This decision was made in consultation
with Borland International, who had previous experience with bound
method references in Delphi Object Pascal.

We believe bound method references are unnecessary because another
design alternative, inner classes, provides equal or superior
functionality. In particular, inner classes fully support the
requirements of user-interface event handling, and have been used to
implement a user-interface API at least as comprehensive as the
Windows Foundation Classes.

We believe bound method references are harmful because they detract
from the simplicity of the Java programming language and the
pervasively object-oriented character of the APIs. Bound method
references also introduce irregularity into the language syntax and
scoping rules. Finally, they dilute the investment in VM technologies
because VMs are required to handle additional and disparate types of
references and method linkage efficiently.


你读过这个:

Delegates are a useful construct in event-based systems. Essentially
Delegates are objects that encode a method dispatch on a specified
object. This document shows how java inner classes provide a more
generic solution to such problems.

What is a Delegate? Really it is very similar to a pointer to member
function as used in C++. But a delegate contains the target object
alongwith the method to be invoked. Ideally it would be nice to be
able to say:

obj.registerHandler(ano.methodOne);

..and that the method methodOne would be called on ano when some specific event was received.

This is what the Delegate structure achieves.

Java Inner Classes

It has been argued that Java provides this
functionality via anonymous inner classes and thus does not need the additional
Delegate construct.

1
2
3
4
5
obj.registerHandler(new Handler() {
        public void handleIt(Event ev) {
            methodOne(ev);
        }
      } );

At first glance this seems correct but at the same time a nuisance.
Because for many event processing examples the simplicity of the
Delegates syntax is very attractive.

General Handler

However, if event-based programming is used in a more
pervasive manner, say, for example, as a part of a general
asynchronous programming environment, there is more at stake.

In such a general situation, it is not sufficient to include only the
target method and target object instance. In general there may be
other parameters required, that are determined within the context when
the event handler is registered.

In this more general situation, the java approach can provide a very
elegant solution, particularly when combined with use of final
variables:

1
2
3
4
5
6
7
8
9
void processState(final T1 p1, final T2 dispatch) {
  final int a1 = someCalculation();

  m_obj.registerHandler(new Handler() {
    public void handleIt(Event ev) {
     dispatch.methodOne(a1, ev, p1);
    }
  } );
}

final * final * final

Got your attention?

Note that the final variables are accessible from within the anonymous
class method definitions. Be sure to study this code carefully to
understand the ramifications. This is potentially a very powerful
technique. For example, it can be used to good effect when registering
handlers in MiniDOM and in more general situations.

By contrast, the Delegate construct does not provide a solution for
this more general requirement, and as such should be rejected as an
idiom on which designs can be based.


我知道这篇文章很老,但是Java 8增加了lambda,以及功能接口的概念,该接口是只有一种方法的任何接口。这些一起提供了与C#委托相似的功能。有关更多信息,请参见此处,或仅查看Google Java Lambdas。
http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html


不,但是使用代理和反射它们是伪造的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  public static class TestClass {
      public String knockKnock() {
          return"who's there?";
      }
  }

  private final TestClass testInstance = new TestClass();

  @Test public void
  can_delegate_a_single_method_interface_to_an_instance() throws Exception {
      Delegator<TestClass, Callable<String>> knockKnockDelegator = Delegator.ofMethod("knockKnock")
                                                                   .of(TestClass.class)
                                                                   .to(Callable.class);
      Callable<String> callable = knockKnockDelegator.delegateTo(testInstance);
      assertThat(callable.call(), is("who's there?"));
  }

关于这个习惯用法的好处是,您可以在创建委托者的时候验证委托方法是否存在,并具有所需的签名(不幸的是,尽管不是在编译时,尽管FindBugs插件可能帮助),然后安全地使用它来委派给各种实例。

有关更多测试和实现,请参见github上的karg代码。


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

好。

这个怎么运作

有一个名为Callback的主体类,以及一个名为WithParms的嵌套类。需要回调的API将把Callback对象作为参数,并在必要时创建一个Callback.WithParms作为方法变量。由于此对象的许多应用程序都是递归的,因此可以很干净地工作。

好。

由于性能仍然是我的重中之重,因此我不需要创建一个一次性对象数组来保存每次调用的参数-毕竟,在大型数据结构中,可能有成千上万个元素,并且在消息处理中在这种情况下,我们可能每秒处理数千个数据结构。

好。

为了确保线程安全,参数数组必须在API方法的每次调用中唯一存在,并且为了提高效率,应在回调的每次调用中使用相同的数组。我需要第二个对象,该对象创建起来很便宜,以便将回调与参数数组绑定以进行调用。但是,在某些情况下,由于其他原因,调用者将已经具有参数数组。由于这两个原因,参数数组不属于Callback对象。同样,调用的选择(将参数作为数组或作为单个对象传递)也使用回调在API手中,从而使API能够使用最适合其内部工作方式的任何调用。

好。

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

好。

以下是使用回调处理目录树中文件的示例。这是一个初始验证阶段,它仅对要处理的文件进行计数,并确保没有超过预定的最大大小。在这种情况下,我们只需使用API??调用创建内联回调。但是,我们将目标方法反映为静态值,因此不会每次都进行反射。

好。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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():

好。

1
2
3
4
5
6
7
8
9
/**
 * 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:
 * [cc lang="java"]    void callback(File dir, File ent);

*

* @return处理的文件数。
* /
static public int processDirectory(File dir,Callback cbk,FileSelector sel){
返回_processDirectory(dir,new Callback.WithParms(cbk,2),sel);
}

静态私有int _processDirectory(文件dir,Callback.WithParms cbk,FileSelector sel){
int cnt = 0;

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

File [] lst =(sel == null?dir.listFiles():dir.listFiles(sel));
if(lst!= null){
for(int xa = 0; xa

这个例子说明了这种方法的优点-将特定于应用程序的逻辑抽象到回调中,并用完全可重用的静态实用程序方法很好地解决了繁琐地递归遍历目录树的工作。而且,我们不必为每种新用途重复付出定义和实现接口的代价。当然,接口的论点是它对实现的内容更为明确(它是强制执行的,而不是简单记录的文档)-但是在实践中,我并未发现正确设置回调定义是一个问题。

好。

定义和实现一个接口并不是真的很糟糕(除非像我一样分发小程序,否则避免创建额外的类实际上很重要),但是真正引人注目的是当一个类中有多个回调时。不仅被迫将它们每个都推入一个单独的内部类中,这在部署的应用程序中增加了开销,而且编程非常繁琐,而且所有样板代码实际上只是"噪音"。

好。

好。


它没有像C#那样的显式delegate关键字,但是您可以通过使用功能接口(即具有唯一一种方法的任何接口)和lambda在Java 8中实现类似的目的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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(),因此可以安全地将其传递给另一个方法(例如delegate(SingleFunc))以调用printMe()方法。


是与否,但是可以考虑Java中的委托模式。该视频教程介绍了活动(片段)之间的数据交换,并且具有使用接口进行委托排序模式的精髓。

Java Interface


否,但是在内部具有相似的行为。

在C#中,委托用于创建单独的入口点,它们的工作原理类似于函数指针。

在Java中,没有什么作为函数指针(从外观上看),但是Java内部需要做同样的事情才能实现这些目标。

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


尽管它几乎不那么干净,但是您可以使用Java代理实现类似C#委托的方式。


Java没有委托,并为此而感到自豪:)。从我在这里阅读的内容中,我发现了两种伪造委托人的方法:
1.反思
2.内部阶层

倒影是slooooow!内部类不涵盖最简单的用例:排序功能。不想赘述,但内部类的解决方案基本上是为要按升序排序的整数数组创建包装类,并为要按降序排序的整数数组创建包装类。


推荐阅读