java Java方法参考
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10085457/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java method reference
提问by Lev
I've some class with these methods:
我有一些带有这些方法的课程:
public class TestClass
{
public void method1()
{
// this method will be used for consuming MyClass1
}
public void method2()
{
// this method will be used for consuming MyClass2
}
}
and classes:
和课程:
public class MyClass1
{
}
public class MyClass2
{
}
and I want HashMap<Class<?>, "question">
where I would store (key: class, value: method) pairs like this ( class "type" is associated with method )
我想HashMap<Class<?>, "question">
在哪里存储 (key: class, value: method) 这样的对(类“类型”与方法相关联)
hashmp.add(Myclass1.class, "question");
and I want to know how to add method references to HashMap (replace "question").
我想知道如何向 HashMap 添加方法引用(替换“问题”)。
p.s. I've come from C# where I simply write Dictionary<Type, Action>
:)
ps 我来自 C#,我只是在那里写Dictionary<Type, Action>
:)
采纳答案by Peter Lawrey
This is feature which is likely to be Java 8. For now the simplest way to do this is to use reflection.
这个特性很可能是 Java 8。现在最简单的方法是使用反射。
public class TestClass {
public void method(MyClass1 o) {
// this method will be used for consuming MyClass1
}
public void method(MyClass2 o) {
// this method will be used for consuming MyClass2
}
}
and call it using
并调用它使用
Method m = TestClass.class.getMethod("method", type);
回答by Daniel Kaplan
Now that Java 8 is out I thought I'd update this question with how to do this in Java 8.
现在 Java 8 出来了,我想我会用如何在 Java 8 中做到这一点来更新这个问题。
package com.sandbox;
import java.util.HashMap;
import java.util.Map;
public class Sandbox {
public static void main(String[] args) {
Map<Class, Runnable> dict = new HashMap<>();
MyClass1 myClass1 = new MyClass1();
dict.put(MyClass1.class, myClass1::sideEffects);
MyClass2 myClass2 = new MyClass2();
dict.put(MyClass2.class, myClass2::sideEffects);
for (Map.Entry<Class, Runnable> classRunnableEntry : dict.entrySet()) {
System.out.println("Running a method from " + classRunnableEntry.getKey().getName());
classRunnableEntry.getValue().run();
}
}
public static class MyClass1 {
public void sideEffects() {
System.out.println("MyClass1");
}
}
public static class MyClass2 {
public void sideEffects() {
System.out.println("MyClass2");
}
}
}
回答by Chandra Sekhar
Method method = TestClass.class.getMethod("method name", type)
回答by M Anouti
You mention in the code comment that each method consumes an object of a certain type. Since this is a common operation, Java already provides you with a functional interfacecalled Consumer
that acts as a way to take an object of a certain type as input and do some actionon it (two words so far that you already mentioned in the question: "consume" and "action").
您在代码注释中提到每个方法都使用某种类型的对象。由于这是一个常见的操作,Java 已经为您提供了一个名为的函数式接口,该接口Consumer
用作将某种类型的对象作为输入并对其执行一些操作的方法(到目前为止您已经在问题中提到了两个词: “消费”和“行动”)。
The map can therefore hold entries where the key is a class such as MyClass1
and MyClass2
, and the value is a consumer of objects of that class:
因此,映射可以保存键是类的条目,例如MyClass1
and MyClass2
,而值是该类的对象的使用者:
Map<Class<T>, Consumer<T>> consumersMap = new HashMap<>();
Since a Consumer
is a functional interface, i.e. an interface with only one abstract method, it can be defined using a lambda expression:
由于 aConsumer
是函数式接口,即只有一个抽象方法的接口,因此可以使用 lambda 表达式定义它:
Consumer<T> consumer = t -> testClass.methodForTypeT(t);
where testClass
is an instance of TestClass
.
哪里testClass
是 的一个实例TestClass
。
Since this lambda does nothing but call an existing method methodForTypeT
, you can use a method referencedirectly:
由于此 lambda 仅调用现有方法methodForTypeT
,因此您可以直接使用方法引用:
Consumer<T> consumer = testClass::methodForTypeT;
Then, if you change the signatures of the methods of TestClass
to be method1(MyClass1 obj)
and method2(MyClass2 obj)
, you would be able to add these method references to the map:
然后,如果您将方法的签名更改TestClass
为method1(MyClass1 obj)
和method2(MyClass2 obj)
,您将能够将这些方法引用添加到映射中:
consumersMap.put(MyClass1.class, testClass::method1);
consumersMap.put(MyClass2.class, testClass::method2);
回答by Yegoshin Maxim
Use interfaces instead of function pointers. So define an interface which defines the function you want to call and then call the interface as in example above. To implement the interface you can use anonymous inner class.
使用接口而不是函数指针。所以定义一个接口来定义你想要调用的函数,然后像上面的例子一样调用这个接口。要实现该接口,您可以使用匿名内部类。
void DoSomething(IQuestion param) {
// ...
param.question();
}
回答by Bohemian
To answer your direct question regarding using a Map
, your proposed classes would be:
要回答有关使用 a 的直接问题Map
,您建议的课程是:
interface Question {} // marker interface, not needed but illustrative
public class MyClass1 implements Question {}
public class MyClass2 implements Question {}
public class TestClass {
public void method1(MyClass1 obj) {
System.out.println("You called the method for MyClass1!");
}
public void method2(MyClass2 obj) {
System.out.println("You called the method for MyClass2!");
}
}
Then your Map
would be:
那么你Map
将是:
Map<Class<? extends Question>, Consumer<Question>> map = new HashMap<>();
and populated like this:
并像这样填充:
TestClass tester = new TestClass();
map.put(MyClass1.class, o -> tester.method1((MyClass1)o)); // cast needed - see below
map.put(MyClass2.class, o -> tester.method2((MyClass2)o));
and used like this:
并像这样使用:
Question question = new MyClass1();
map.get(question.getClass()).accept(question); // calls method1
The above works OK, but the problem is that there's no way to connect the type of the keyof the map with the type of its value, ie you can't use generics to properly type the value of the consumer and so use a method reference:
以上工作正常,但问题是没有办法将映射的键的类型与其值的类型连接起来,即您不能使用泛型正确键入消费者的值,因此使用方法参考:
map.put(MyClass1.class, tester::method1); // compile error
that's why you need to cast the object in the lambda to bind to the correct method.
这就是为什么您需要在 lambda 中强制转换对象以绑定到正确的方法。
There's also another problem. If someone creates a new Question class, you don't know until runtimethat there isn't an entry in the Map for that class, andyou have to write code like if (!map.containsKey(question.getClass())) { // explode }
to handle that eventuality.
还有一个问题。如果有人创建了一个新的 Question 类,您直到运行时才知道Map 中没有该类的条目,并且您必须编写类似if (!map.containsKey(question.getClass())) { // explode }
处理这种可能性的代码。
But there is an alternative...
但是有一个替代...
There is another pattern that doesgive you compile time safety, and means you don't need to write any code to handle "missing entries". The pattern is called Double Dispatch(which is part of the Visitorpattern).
还有另一种模式确实可以为您提供编译时安全性,这意味着您无需编写任何代码来处理“丢失的条目”。该模式称为双调度(它是访问者模式的一部分)。
It looks like this:
它看起来像这样:
interface Tester {
void consume(MyClass1 obj);
void consume(MyClass2 obj);
}
interface Question {
void accept(Tester tester);
}
public class TestClass implements Tester {
public void consume(MyClass1 obj) {
System.out.println("You called the method for MyClass1!");
}
public void consume(MyClass2 obj) {
System.out.println("You called the method for MyClass2!");
}
}
public class MyClass1 implements Question {
// other fields and methods
public void accept(Tester tester) {
tester.consume(this);
}
}
public class MyClass2 implements Question {
// other fields and methods
public void accept(Tester tester) {
tester.consume(this);
}
}
And to use it:
并使用它:
Tester tester = new TestClass();
Question question = new MyClass1();
question.accept(tester);
or for many questions:
或对于许多问题:
List<Question> questions = Arrays.asList(new MyClass1(), new MyClass2());
questions.forEach(q -> q.accept(tester));
This pattern works by putting a callback into the target class, which can bind to the correct method for handling that class for the this
object.
这种模式的工作原理是将回调放入目标类中,目标类可以绑定到正确的方法来处理this
对象的该类。
The benefit of this pattern is if another Question class is created, it is required to implement the accept(Tester)
method, so the Question implementer will not forget to implement the callback to the Tester, andautomatically checks that Testers can handle the new implementation, eg
这种模式的好处是如果再创建一个Question类,就需要实现该accept(Tester)
方法,这样Question实现者不会忘记实现对Tester的回调,并自动检查Tester是否可以处理新的实现,例如
public class MyClass3 implements Question {
public void accept(Tester tester) { // Questions must implement this method
tester.consume(this); // compile error if Tester can't handle MyClass3 objects
}
}
Also note how the two classes don't reference each other - they only reference the interface, so there's total decoupling between Tester and Question implementations (which makes unit testing/mocking easier too).
还要注意这两个类如何不相互引用 - 它们只引用interface,因此 Tester 和 Question 实现之间完全解耦(这也使单元测试/模拟更容易)。
回答by nd.
While you can store java.lang.reflect.Method
objects in your map, I would advise against this: you still need to pass the object that is used as the this
reference upon invocation, and using raw strings for method names may pose problems in refactoring.
虽然您可以java.lang.reflect.Method
在映射中存储对象,但我建议您不要这样做:您仍然需要this
在调用时传递用作引用的对象,并且使用原始字符串作为方法名称可能会在重构中造成问题。
The cannonical way of doing this is to extract an interface (or use an existing one) and use anonymous classes for storing:
这样做的规范方法是提取接口(或使用现有接口)并使用匿名类进行存储:
map.add(MyClass1.class, new Runnable() {
public void run() {
MyClass1.staticMethod();
}
});
I must admit that this is much more verbose than the C#-variant, but it is Java's common practice - e.g. when doing event handling with Listeners. However, other languages that build upon the JVM usually have shorthand notations for such handlers. By using the interface-approach, your code is compatible with Groovy, Jython, or JRuby and it is still typesafe.
我必须承认,这比 C# 变体要冗长得多,但这是 Java 的常见做法 - 例如,在使用侦听器进行事件处理时。但是,构建在 JVM 上的其他语言通常具有此类处理程序的速记符号。通过使用接口方法,您的代码与 Groovy、Jython 或 JRuby 兼容,并且仍然是类型安全的。
回答by Nishant
Have you tried Method
object? refer:
你试过Method
对象吗?参考:
回答by Gee Bee
Your question
你的问题
Given your classes with some methods:
鉴于您的课程有一些方法:
public class MyClass1 {
public void boo() {
System.err.println("Boo!");
}
}
and
和
public class MyClass2 {
public void yay(final String param) {
System.err.println("Yay, "+param);
}
}
Then you can get the methods via reflection:
然后你可以通过反射获取方法:
Method method=MyClass1.class.getMethod("boo")
When calling a method, you need to pass a class instance:
调用方法时,需要传递一个类实例:
final MyClass1 instance1=new MyClass1();
method.invoke(instance1);
To put it together:
把它放在一起:
public class Main {
public static void main(final String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
final Map<Class<?>,Method> methods=new HashMap<Class<?>,Method>();
methods.put(MyClass1.class,MyClass1.class.getMethod("boo"));
methods.put(MyClass2.class,MyClass2.class.getMethod("yay",String.class));
final MyClass1 instance1=new MyClass1();
methods.get(MyClass1.class).invoke(instance1);
final MyClass2 instance2=new MyClass2();
methods.get(MyClass2.class).invoke(instance2,"example param");
}
}
Gives:
Boo!
Yay, example param
给:
嘘!
是的,示例参数
Watch out for the following gotchas:
请注意以下问题:
- hardcoded method name as a string - this is very hard to avoid
- it is reflection, so accessing to the metadata of the class in runtime. Prone to a lot of exceptions (not handled in the example)
- you need to tell not only the method name, but the parameter types as well to access to one method. This is because method overloading is standard, and this is the only way to pick the right overloaded method.
- watch out when calling a method with parameters: there is no compile time parameter type check.
- 作为字符串的硬编码方法名称 - 这很难避免
- 它是反射,因此在运行时访问类的元数据。容易出现很多异常(示例中未处理)
- 您不仅需要告知方法名称,还需要告知参数类型以访问一种方法。这是因为方法重载是标准的,这是选择正确的重载方法的唯一方法。
- 调用带参数的方法时要小心:没有编译时参数类型检查。
An alternative answer
另一种答案
I guess what you're looking for is a simple listener: i.e. a way to call a method from another class indirectly.
我猜您正在寻找的是一个简单的侦听器:即一种从另一个类间接调用方法的方法。
public class MyClass1 implements ActionListener {
@Override
public void actionPerformed(final ActionEvent e) {
System.err.println("Boo!");
}
}
and
和
public class MyClass2 implements ActionListener {
@Override
public void actionPerformed(final ActionEvent e) {
System.err.println("Yay");
}
}
using as:
用作:
public class Main {
public static void main(final String[] args) {
final MyClass1 instance1=new MyClass1();
final MyClass2 instance2=new MyClass2();
final Map<Class<?>,ActionListener> methods=new HashMap<Class<?>,ActionListener>();
methods.put(MyClass1.class,instance1);
methods.put(MyClass2.class,instance2);
methods.get(MyClass1.class).actionPerformed(null);
methods.get(MyClass2.class).actionPerformed(null);
}
}
This is called the listener pattern. I dared to reuse the ActionListener from Java Swing, but in fact you can very easily make your own listeners by declaring an interface with a method. MyClass1, MyClass2 will implement the method, and then you can call it just like a... method.
这称为侦听器模式。我敢于重用 Java Swing 中的 ActionListener,但实际上,您可以通过使用方法声明接口来轻松创建自己的侦听器。MyClass1、MyClass2 将实现该方法,然后您可以像调用...方法一样调用它。
No reflection, no hardcoded strings, no mess. (The ActionListener allows passing one parameter, which is tuned for GUI apps. In my example I just pass null.)
没有反射,没有硬编码的字符串,没有混乱。(ActionListener 允许传递一个参数,该参数针对 GUI 应用程序进行了调整。在我的示例中,我只传递了 null。)