- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我有两个接口(interface) Query
和 Filter
(Query
是示例中的一个类,为了简化,我现在有 1 个查询),我现在想要编写函数 Query.applyFilter()
取决于 Filter 是真实的是什么,即 NameFilter
和 DateFilter
和 every other 过滤器。
我的解决方案如下:
interface Filter {
public abstract void modifyQuery(Query query);
};
class NameFilter implements Filter{
public void modifyQuery(Query query){
query.applyFilter(this);
}
};
class DateFilter implements Filter{
public void modifyQuery(Query query){
query.applyFilter(this);
}
};
class Query {
public void applyFilter(Filter filter){
filter.modifyQuery(this);
}
void applyFilter(NameFilter* filter) {
//"applying NameFilter";
}
void applyFilter(DateFilter* filter) {
//apply dateFilter
}
}
那么好吧,我需要为每个 Filter 类重写 modifyQuery()
实现。
然后,我有解决方案如何在 C++ 中避免它:我们使用模板并在 modifyQuery()
中强制转换:
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <vector>
using namespace std;
class Query;
class IFilter
{
public:
virtual void modifyQuery(Query* query) = 0;
};
template <typename T>
class Filter : public IFilter
{
public:
virtual void modifyQuery(Query* query);
};
class DateFilter;
class NameFilter;
class Query
{
public:
void applyFilter(IFilter* filter)
{
cout << "applying Filter" << endl;
filter->modifyQuery(this);
}
void applyFilter(NameFilter* filter)
{
cout << "applying NameFilter" << endl;
}
void applyFilter(DateFilter* filter)
{
cout << "applying DateFilter" << endl;
}
};
template <typename T>
void Filter<T>::modifyQuery(Query* query)
{
query->applyFilter(dynamic_cast<T*> (this));
}
class DateFilter : public Filter<DateFilter>
{
};
class NameFilter : public Filter<NameFilter>
{
};
int main()
{
Query* query = new Query();
IFilter* nameFilter = new NameFilter();
IFilter* dateFilter = new DateFilter();
std::vector<IFilter*> filterList;
filterList.push_back(nameFilter);
filterList.push_back(dateFilter);
for (int i = 0; i < 2; ++i)
{
query->applyFilter(filterList[i]);
}
return 0;
}
但我无法在 Java 中使用此解决方案,因为泛型的操作方式与模板不同。在 Java 中可以使用什么来避免复制粘贴?
最佳答案
在寻找正确的方法时,Java 不会考虑方法参数的运行时类型。 Java 只是对变量类型而不是值进行推理。
解决方案:
Java 6 注释可用于注释方法并实现多方法和值分派(dispatch)。所有这些都可以在运行时完成,无需任何特殊的编译或预处理,并且使用起来仍然相当用户友好。
我们需要引入两个“简单”的注解来进行注解:
Methods
:这个方法实现了什么multimethod?
参数
:我们应该分配什么值?
然后我们可以处理注释并构建一个实现特定多方法的方法列表。这个列表需要排序,以便最具体的方法排在最前面。 “最具体”意味着对于每个方法参数(从左到右),参数类型/值更特殊(例如,它是一个子类或与指定值再次匹配)。调用多方法意味着调用最具体的适用方法。 “适用”意味着方法原型(prototype)与实际运行时参数相匹配,“最具体”意味着我们可以简单地搜索排序列表并找到第一个适用的。
注释处理可以封装在一个类中,然后可以在用户定义的方法中使用该类,该方法将使用实际的运行时参数简单地调用多方法调度代码。
实现
接口(interface)Multi实现了一个运行时方法注解,用于标记多方法:
package jmultimethod;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Retention;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Multi {
public String value();
}
接口(interface) V 实现了一个运行时参数注释,用于指定调度值:
package jmultimethod;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface V {
public String value();
}
Multimethod 代码如下:
package jmultimethod;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
public class Multimethod {
protected String name;
protected final ArrayList<Method> methods = new ArrayList<Method>();
protected final MethodComparator methodComparator = new MethodComparator();
public Multimethod(String name, Class... classes) {
this.name = name;
for(Class c: classes) {
add(c);
}
}
public void add(Class c) {
for(Method m: c.getMethods()) {
for(Annotation ma: m.getAnnotations()) {
if(ma instanceof Multi) {
Multi g = (Multi) ma;
if(this.name.equals(g.value())) {
methods.add(m);
}
}
}
}
sort();
}
protected void sort() {
Method[] a = new Method[methods.size()];
methods.toArray(a);
Arrays.sort(a, methodComparator);
methods.clear();
for(Method m: a) {
methods.add(m);
}
}
protected class MethodComparator implements Comparator<Method> {
@Override
public int compare(Method l, Method r) {
// most specific methods first
Class[] lc = l.getParameterTypes();
Class[] rc = r.getParameterTypes();
for(int i = 0; i < lc.length; i++) {
String lv = value(l, i);
String rv = value(r, i);
if(lv == null) {
if(rv != null) {
return 1;
}
}
if(lc[i].isAssignableFrom(rc[i])) {
return 1;
}
}
return -1;
}
}
protected String value(Method method, int arg) {
Annotation[] a = method.getParameterAnnotations()[arg];
for(Annotation p: a) {
if(p instanceof V) {
V v = (V) p;
return v.value();
}
}
return null;
}
protected boolean isApplicable(Method method, Object... args) {
Class[] c = method.getParameterTypes();
for(int i = 0; i < c.length; i++) {
// must be instanceof and equal to annotated value if present
if(c[i].isInstance(args[i])) {
String v = value(method, i);
if(v != null && !v.equals(args[i])) {
return false;
}
} else {
if(args[i] != null || !Object.class.equals(c[i])) {
return false;
}
}
}
return true;
}
public Object invoke(Object self, Object... args) {
Method m = null; // first applicable method (most specific)
for(Method method: methods) {
if(isApplicable(method, args)) {
m = method;
break;
}
}
if(m == null) {
throw new RuntimeException("No applicable method '" + name + "'.");
}
try {
return m.invoke(self, args);
} catch (Exception e) {
throw new RuntimeException("Method invocation failed '" + name + "'.");
}
}
}
要使用多重方法,用户代码必须:
用多方法名称注释方法,例如
@Multi("myMultimethod")
注释方法的名称可以是 Java 喜欢的任何名称。它很可能与多方法名称不同,因为某些方法可能具有足够相似的原型(prototype)以导致 Java 编译器的名称冲突(并且可能是因为编译器可能有 null 值问题)。此外,该方法应该对 Multimethod 类可见(例如公开)。
通过创建 Multimethod 对象来处理注释,例如
protected Multimethod mm = new Multimethod("myMultimethod", getClass());
根据需要使用通用参数定义多方法“入口点”方法。此方法使用上面创建的 Multimethod 对象进行调度,例如
public void myMultimethod(Object X, Object Y) {
mm.invoke(this, X, Y);
}
然后,可以像调用任何普通 Java 方法一样调用多方法,例如
myMultimethod(1, null);
限制:
值分配仅适用于 Java 注释支持的值,例如字符串类型的值。
以下代码基于Multiple Dispatch example
package jmultimethod;
public class AsteroidTest {
class Asteroid {}
class Spaceship {}
@Multi("collide")
public void collideOO(Object X, Object Y) {
log("?? Bang, what happened? ", X, Y);
}
@Multi("collide")
public void collideAA(Asteroid X, Asteroid Y) {
log("AA Look at the beautiful fireworks! ", X, Y);
}
@Multi("collide")
public void collideAS(Asteroid X, Spaceship Y) {
log("AS Is it fatal? ", X, Y);
}
@Multi("collide")
public void collideSA(Spaceship X, Asteroid Y) {
log("SA Is it fatal? ", X, Y);
}
@Multi("collide")
public void collideSS(Spaceship X, Spaceship Y) {
log("SS Who's fault was it? ", X, Y);
}
@Multi("collide")
public void collide1S(String X, Spaceship Y) {
log("1S any string? ", X, Y);
}
@Multi("collide")
public void collide2S(@V("hi") String X, Spaceship Y) {
log("2S 'hi' value? ", X, Y);
}
protected Multimethod mm = new Multimethod("collide", getClass());
public void collide(Object X, Object Y) {
mm.invoke(this, X, Y);
}
public void run() {
Object A = new Asteroid();
Object S = new Spaceship();
collide(A, A);
collide(A, S);
collide(S, A);
collide(S, S);
collide(A, 1);
collide(2, A);
collide(S, 3);
collide(4, S);
collide(5, null);
collide(null, null);
collide("hi", S);
collide("hello", S);
}
public void log(Object... args) {
for(Object o: args) {
if(o instanceof String) {
System.out.print(" " + (String) o);
} else {
System.out.print(" " + o);
}
}
System.out.println();
}
public static void main(String[] args) throws Exception {
AsteroidTest t = new AsteroidTest();
t.run();
}
}
程序输出(部分编辑以适应屏幕)是:
AA Look at the beautiful fireworks! Asteroid@1f24bbbf Asteroid@1f24bbbf
AS Is it fatal? Asteroid@1f24bbbf Spaceship@24a20892
SA Is it fatal? Spaceship@24a20892 Asteroid@1f24bbbf
SS Who's fault was it? Spaceship@24a20892 Spaceship@24a20892
?? Bang, what happened? Asteroid@1f24bbbf 1
?? Bang, what happened? 2 Asteroid@1f24bbbf
?? Bang, what happened? Spaceship@24a20892 3
?? Bang, what happened? 4 Spaceship@24a20892
?? Bang, what happened? 5 null
?? Bang, what happened? null null
2S 'hi' value? hi Spaceship@24a20892
1S any string? hello Spaceship@24a20892
关于java - Java中的双重调度自动化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14033090/
我正在编写一个具有以下签名的 Java 方法。 void Logger(Method method, Object[] args); 如果一个方法(例如 ABC() )调用此方法 Logger,它应该
我是 Java 新手。 我的问题是我的 Java 程序找不到我试图用作的图像文件一个 JButton。 (目前这段代码什么也没做,因为我只是得到了想要的外观第一的)。这是我的主课 代码: packag
好的,今天我在接受采访,我已经编写 Java 代码多年了。采访中说“Java 垃圾收集是一个棘手的问题,我有几个 friend 一直在努力弄清楚。你在这方面做得怎么样?”。她是想骗我吗?还是我的一生都
我的 friend 给了我一个谜语让我解开。它是这样的: There are 100 people. Each one of them, in his turn, does the following
如果我将使用 Java 5 代码的应用程序编译成字节码,生成的 .class 文件是否能够在 Java 1.4 下运行? 如果后者可以工作并且我正在尝试在我的 Java 1.4 应用程序中使用 Jav
有关于why Java doesn't support unsigned types的问题以及一些关于处理无符号类型的问题。我做了一些搜索,似乎 Scala 也不支持无符号数据类型。限制是Java和S
我只是想知道在一个 java 版本中生成的字节码是否可以在其他 java 版本上运行 最佳答案 通常,字节码无需修改即可在 较新 版本的 Java 上运行。它不会在旧版本上运行,除非您使用特殊参数 (
我有一个关于在命令提示符下执行 java 程序的基本问题。 在某些机器上我们需要指定 -cp 。 (类路径)同时执行java程序 (test为java文件名与.class文件存在于同一目录下) jav
我已经阅读 StackOverflow 有一段时间了,现在我才鼓起勇气提出问题。我今年 20 岁,目前在我的家乡(罗马尼亚克卢日-纳波卡)就读 IT 大学。足以介绍:D。 基本上,我有一家提供簿记应用
我有 public JSONObject parseXML(String xml) { JSONObject jsonObject = XML.toJSONObject(xml); r
我已经在 Java 中实现了带有动态类型的简单解释语言。不幸的是我遇到了以下问题。测试时如下代码: def main() { def ks = Map[[1, 2]].keySet()
一直提示输入 1 到 10 的数字 - 结果应将 st、rd、th 和 nd 添加到数字中。编写一个程序,提示用户输入 1 到 10 之间的任意整数,然后以序数形式显示该整数并附加后缀。 public
我有这个 DownloadFile.java 并按预期下载该文件: import java.io.*; import java.net.URL; public class DownloadFile {
我想在 GUI 上添加延迟。我放置了 2 个 for 循环,然后重新绘制了一个标签,但这 2 个 for 循环一个接一个地执行,并且标签被重新绘制到最后一个。 我能做什么? for(int i=0;
我正在对对象 Student 的列表项进行一些测试,但是我更喜欢在 java 类对象中创建硬编码列表,然后从那里提取数据,而不是连接到数据库并在结果集中选择记录。然而,自从我这样做以来已经很长时间了,
我知道对象创建分为三个部分: 声明 实例化 初始化 classA{} classB extends classA{} classA obj = new classB(1,1); 实例化 它必须使用
我有兴趣使用 GPRS 构建车辆跟踪系统。但是,我有一些问题要问以前做过此操作的人: GPRS 是最好的技术吗?人们意识到任何问题吗? 我计划使用 Java/Java EE - 有更好的技术吗? 如果
我可以通过递归方法反转数组,例如:数组={1,2,3,4,5} 数组结果={5,4,3,2,1}但我的结果是相同的数组,我不知道为什么,请帮助我。 public class Recursion { p
有这样的标准方式吗? 包括 Java源代码-测试代码- Ant 或 Maven联合单元持续集成(可能是巡航控制)ClearCase 版本控制工具部署到应用服务器 最后我希望有一个自动构建和集成环境。
我什至不知道这是否可能,我非常怀疑它是否可能,但如果可以,您能告诉我怎么做吗?我只是想知道如何从打印机打印一些文本。 有什么想法吗? 最佳答案 这里有更简单的事情。 import javax.swin
我是一名优秀的程序员,十分优秀!