我正在尝试加载方法Customer。密码和客户。cypherCBC方法从我的类配置。客户类是从不同的环境中呈现的,所以很少有环境具有cypherCBC()和cypher()方法,很少有环境仅具有cypher()方法。
现在我想检查如果cypherCBC如果没有在客户类然后加载cypher()方法。我的功能到此为止;
try {
Class<?> customerClass = Class.forName("com.myapp.impl.service.Customer");
Object obj = customerClass.newInstance();
//here getting "NoSuchMethodException" exception
Method methodCBC = customerClass.getDeclaredMethod("cypherCBC", String.class); //line - 7
if(methodCBC.getName().equals("cypherCBC")){
methodCBC.invoke(obj, new String(dbshPass));
System.out.println("CYPHER_CBC: "
+ methodCBC.invoke(obj, new String(dbshPass)));
}else{
Method method = customerClass.getDeclaredMethod("cypher", String.class);
method.invoke(obj, new String(dbshPass));
System.out.println("CYPHER: " + method.invoke(obj, new String(dbshPass)));
}
}catch (Exception e){
e.printStackTrace();
}
在第7行出现错误。
NoSuchMethodException:com.myapp.impl.service.Customer.cypherCBC(以)
这意味着对于特定的环境类Customer没有cypherCBC()方法,但理想情况下,它应该出现在else部分并执行cypher()方法。
Class<?> client = null;
Object obj = null;
try{
client = Class.forName("com.myapp.impl.service.Client");
obj = client.newInstance();
}catch (InstantiationException ex) {
System.err.println("Not able to create Instance of Class");
} catch (IllegalAccessException ex) {
System.err.println("Not able to access Class");
} catch (ClassNotFoundException ex) {
System.err.println("Not able to find Class");
}
try {
Method methodCBC = client.getDeclaredMethod("cypherCBC", String.class);
System.out.println("CYPHER_CBC: " + methodCBC.invoke(obj, new String(dbshPass)));
}catch (NoSuchMethodException ex) {
System.err.println("Not able to find Method on class");
ex.printStackTrace();
} catch (Exception e){
e.printStackTrace();
}
这正是所期望的:当不存在满足您的规范的方法时,getDeclaredMethod()会抛出异常。你想知道,它抛出一个异常,如果所需的方法是缺失的?提示:下次最好阅读javadoc。不要假设某件事能做另一件事,但要验证你的假设!
另外:再读一遍你的代码。它在做什么?你在问"给我命名为'foo'的方法"。然后,你的下一步是问那个方法"你的名字是'foo'吗"因此,即使不阅读javadoc,也应该很清楚您的逻辑是有缺陷的。
作为解决方案,您可以自己实现非抛出查找,如
private Method lookupCypher(Class<?> client, String methodName) {
for (Method declaredMethod : client.getDeclardMethods()) {
if (declaredMethod.getName().equals(methodName)) {
Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
if (parameterTypes.length == 1 && parameterTypes[0].equals(String.class)) {
// so declaredMethod has the given name, and takes one string as argument!
return declaredMethod;
}
}
// our search didn't reveal any matching method!
return null;
}
使用该帮助方法,您可以将代码重写为:
Method toInvoke = lookupCypher(client, "cypherCBC");
if (toInvoke == null) {
toInvoke = lookupCypher(client, "cypher");
}
toInvoke(obj, new String ...
或者,带着猎人的想法;一个更像"OO"的版本:
interface CustomerCypherWrapper {
void cypher(String phrase);
}
class NewCustomerWrapper() implements CustomerCypherWrapper {
@Override
void cypher(String phrase) {
new Customer.cypherCBC(phrase);
}
}
class oldCustomerWrapper() implements CustomerCypherWrapper {
@Override
void cypher(String phrase) {
new Customer.cypher(phrase);
}
}
和你的客户端代码归结为:
CustomerCypherWrapper wrapper =
(lookupCypher(..., "cypherCBC") == null)
? new NewCustomerWrapper()
: new OldCustomerWrapper();
wrapper.cypher();
[我希望你注意到我的版本A)更容易阅读,B)不再包含任何重复的代码。)
是的,查找方法的另一种实现可以像
这样private Method lookupCyper(Client<?>, String methodName) {
try {
return client.getDeclaredMethod(methodName, String.class);
} catch ....
and return null;
}
... return your public cypherCBC method
但这在Java中是"不常见的做法"。在Java中,我们请求许可;而不是原谅。不像其他语言
如果你用一个有两个方法的Customer类来编译应用程序,你可以使用反射一次来检查cypherCBC方法是否在运行时可用,然后你可以保持该状态,你可以调用该方法而不使用反射
if(newVersion)
{
customer.cypherCBC(arg);
}
else
{
customer.cypher(arg);
}
但是要编写更好的应用程序,您应该使用两个版本基线。尽管这只是一小段代码,但您应该设置另一个模块来隐藏这个Customer类及其交互,该模块应该有两个版本。但是你的主模块只有一个版本。现在,当您交付应用程序时,应该根据目标环境的兼容性将产品打包为正确的版本基线。
尽管反射是有效的(正如在其他答案中解释的那样)。如果您可以控制Customer
类,您可以尝试非反射方法。
interface CBCCypherable {
public String cypherCBC(String pass);
}
你现在可以有两个版本的Customer
类,一个实现CBCCypherable
和一个不。当你调用它时,它看起来像这样:
Customer c = new Customer();
if (c instanceof CBCCypherable) {
((CBCCypherable)c).cypherCBC(pass);
} else {
c.cypher(pass);
}
使用这种解决方案得到的代码要简单得多,并且作为奖励,编译器将检查您是否使用了正确的方法名和参数类型。不像反射,这是你的全部工作,你必须运行代码来发现是否有问题。
注。