public class ExampleClass {
public static void main(String[] args) {
// TODO Auto-generated method stub
Horse hr1 = new Horse();
Horse hr2 = new Horse();
Horse hr3 = new Horse();
Horse hr4 = new Horse();
Set hrSet = new HashSet();
hrSet.add(hr1);
hrSet.add(hr2);
hrSet.add(hr3);
hrSet.add(hr4);
Horse hr;
String hor = "sher_pkg.Horse";
callHorse(hrSet,hor);
}
public static void callHorse(Set xSet,String clsName){
try {
Class hrt = Class.forName(clsName);
Iterator hritr = xSet.iterator();
while(hritr.hasNext()){
exam(hrt.cast(hritr.next()));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void exam(Object obj){ //I want to use exam(Horse hrr)
System.out.println(obj);
}
}
此处考试功能的参数为Object
。但是我希望参数为Horse
...那么必须在“exam(hrt.cast(hritr.next()))
”方法调用中进行哪些更改?我不想在Horse
中明确使用类名callHorse()
...那么我该怎么办?
由于
答案 0 :(得分:3)
注意:序列为“if (x instanceof MyClass)
的代码通常表明您没有充分使用多态性。通常可以重构代码以摆脱测试它的需要。但我会忽略这一点,为了回答问题。
您可以执行您要执行的操作,但不能在没有更改代码的情况下执行操作。方法重载不能满足您的需要,因为在Java中,方法重载是在编译时决定的。因此,如果类中有两个方法,其中两个方法具有相同的名称,相同的返回类型,但参数类型不同,则调用此重载方法的任何代码都必须明确调用哪个方法。由于使用了显式强制转换,您当前的代码使用它提供的类型执行此操作,但完全动态版本没有。如果在运行时决定方法重载,那么您的代码将按照您的意愿执行。但是因为它是在编译时决定的,所以你的代码不会编译。
要解决您的问题,您可以使用泛型,也可以重构代码。首先,我将介绍一个测试工具,它显示了您开始使用的非常简化的版本:
public class Test {
public void test(Object obj) {
if (obj instanceof Horse) {
Horse c = (Horse) obj;
noise(c);
}
if (obj instanceof Cow) {
Cow c = (Cow) obj;
noise(c);
}
}
public void noise(Horse h) {
System.out.println("Neigh");
}
public void noise(Cow c) {
System.out.println("Moo");
}
public static void main(String[] args) {
Object o1 = new Horse();
Object o2 = new Cow();
Test tester = new Test();
tester.test(o1);
tester.test(o2);
}
}
class Horse {}
class Cow {}
此代码运行并执行您期望的操作。它打印“Neigh”,然后是“Moo”。
您正在尝试替换
if (obj instanceof Horse) {
Horse c = (Horse) obj;
noise(c);
}
带
if (obj instanceof Horse) {
handleNoise(obj, Horse.class);
}
然后添加处理它的方法(简化):
void handleNoise(Object obj, Class clazz) {
noise(clazz.cast(obj));
}
正如我之前所说,这不起作用noise
的重载是在编译时决定的。编译器看到您正在进行转换,但在编译时不知道该类型是什么。所以它无法选择重载和编译失败。
解决此问题的最佳方法是使用多态,因为多态性是在运行时决定的。也就是说,让所有这些类实现一些接口,然后将有问题的代码移动到各个类中。以下是执行此操作的示例:
public class Test {
public void test(Animal obj) {
obj.noise();
}
public static void main(String[] args) {
Animal o1 = new Horse();
Animal o2 = new Cow();
Test tester = new Test();
tester.test(o1);
tester.test(o2);
}
}
interface Animal {
void noise();
}
class Horse implements Animal {
public void noise() {
System.out.println("Neigh");
}
}
class Cow implements Animal {
public void noise() {
System.out.println("Moo");
}
}
注意测试方法有多简单!如果您可以让每个项目实现一个处理下面所谓的stringProp的接口,那么您可以简化部分方式:
if (obj instanceof Cust) {
loopOverSet(c.getCustPhonSet());
} else if (obj instanceof Name) {
loopOverSet(c.getCustNameSet());
}
// and so on for the rest...
然后添加方法:
void loopOVerSet(Set cxSet) {
if (cxSet != null && cxSet.size() > 0) {
Iterator cxSetIterator = cxSet.iterator();
while (cxSetIterator.hasNext())
{
((StringProp)cxSetIterator.next()).stringProp();
}
}
}
这假设先前重载的方法stringProp
已被移动到各个类CustPhone
和CustName
等等,并且这些类都实现了我调用的一些接口StringProp
此接口定义方法stringProp()
。由于此代码使用覆盖而不是重载,因此将在运行时决定。
答案 1 :(得分:1)
你可能想看一下泛型。
public static void callHorse(Set<Horse> xSet) {
Iterator<Horse> hritr = xSet.iterator();
while (hritr.hasNext()) {
exam(hritr.next());
}
}
public static void exam(Horse obj) { //I want to use exam(Horse hrr)
System.out.println(obj);
}
当然,在您的示例中,您始终只能投射对象。为什么你不想那样做是超出我的。
答案 2 :(得分:1)
当你说:
exam(Horse hrr)
您告诉编译器您希望它检查对exam()的所有调用,并确保每个调用都提供一个Horse对象作为参数。但是,在callHorse()中,您使用动态转换参数调用exam(),并且编译器无法检查参数。
您可以通过使用反射并动态调用exam()方法来解决此问题。
答案 3 :(得分:0)
你可以在函数调用中明确地转换 -
try {
Class hrt = Class.forName(clsName);
Iterator hritr = xSet.iterator();
while(hritr.hasNext()){
exam((Horse)hrt.cast(hritr.next()));
}
}
但我不确定你在这里想要实现的目标 - 如果你正在编写明确引用Horses的代码,为什么还需要从字符串中动态确定类类型?
答案 4 :(得分:0)
首先,你的集合应该使用泛型或明确定义为仅持有Horse Objects。
(final Set xSet<Horse>, final String clsName){
...}
修复此问题,您已解决了90%的问题。
答案 5 :(得分:0)
看起来你的设计对Java来说是错误的,你不能直接做你想要的。
也许您需要重塑代码才能使用访问者模式?如果做不到这一点,您需要解释您的要求,而不是您想要使用的解决方案。通过这种方式,我们可以告诉您适合您需求的Java解决方案。
答案 6 :(得分:0)
我不确定在callHorse方法中避免引用“Horse”是可能的或可取的。从ClassNotFoundException之后的printstacktrace判断,如果由于某种原因找不到类,则抛出一个硬错误。
出于同样的原因,难道你不能仅仅投射到“马”,然后如果Set中的某些东西不是马,那就赶上classcastexception吗?
你能解释为什么你需要传入classname而不是类吗?
也许你也可以使用方法重载,但我必须对此进行测试,因为在这种情况下我不完全确定优先级是什么。
答案 7 :(得分:0)
如果您使用Class.cast()
使用您要传递给另一个函数的参数进行动态转换,那么在编译时,您所传递的类型一无所知。这就是为什么你不能使用Horse
作为定义方法的参数类型,而是以你自己的方式使用反射来调用方法。除了验证之外,你的演员阵容很少 - 只要你没有得到Exception
- 你传入的集合完全由你传递的Class
成员组成。
请注意,{5}中引入了Class.cast()
方法,这意味着如果您有权访问Class.cast()
,则可以访问泛型。泛型可以帮助清理,虽然它们无法解决您试图解决的问题。
使用Java 5 for
循环,您可以按如下方式重写循环:
public static void callHorse(Set<?> xSet, String clsName) {
try {
Class<?> hrt = Class.forName(clsName);
for (Object x : xSet) {
exam(hrt.cast(x));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
此版本不那么混乱,使您的演员阵容更加明显。你正在铸造一个完全任意的类型。只要可以从类路径加载类定义,转换可以是任何类型。因此,如果您的exam()
方法接受Horse
参数,则编译器知道它无法保证调用将成功并且代码无法编译。
即使您尝试重载,它也无法正常工作。也就是说,如果您创建方法:
public static void exam(Object obj) {
System.out.println("Object " + obj);
}
public static void exam(Horse obj) {
System.out.println("Horse " + obj);
}
exam(Object)
方法始终是被调用的方法。试试吧。
最重要的是,你要做的事情是无法完成的。在我们为您提供帮助之前,您需要向我们提供有关您的目标的更多信息。
答案 8 :(得分:0)
为什么不这样写呢?你的要求究竟是什么?
public static void main(String[] args) {
Set<Horse> horses = new HashSet<Horse>();
horses.add(new Horse());
horses.add(new Horse());
horses.add(new Horse());
horses.add(new Horse());
callHorse(horses);
}
public static void callHorse(Set<Horse> horses) {
for (Horse horse : horses) {
exam(horse);
}
}
public static void exam(Horse horse) {
System.out.println(horse);
}
根据你在exam()方法中的操作,将它作为Horse的实例方法也是有意义的,如下所示:
public static void main(String[] args) {
Set<Horse> horses = new HashSet<Horse>();
horses.add(new Horse());
horses.add(new Horse());
horses.add(new Horse());
horses.add(new Horse());
examineHorses(horses);
}
public static void examineHorses(Set<Horse> horses) {
for (Horse horse : horses) {
horse.examine();
}
}
// in Horse.java
public class Horse {
public void examine() {
System.out.println(this);
}
...
}
答案 9 :(得分:0)
您的真正目标是拥有多个版本的exam()方法,它们将不同类型作为参数,并动态选择运行时所需的版本吗?
您可以使用反射显式执行此操作。这是一个示例程序。
import java.lang.reflect.*;
public class Test {
public static void exam( Object o ) {
System.out.println( "Object version called" );
}
public static void exam( Test t ) {
System.out.println( "Test version called" );
}
public static void main (String[] args) {
try {
// Create an instance of Test but reference it as an Object
Object untypedTest = new Test();
// Calling exam directly will invoke the Object version
exam( untypedTest );
// But if we use reflection to select the version of exam
// that takes the desired class name, we can invoke it without
// even explicitly casting
String className = "Test";
Class[] examMethodParams = { Class.forName( className ) };
Method examMethod = Test.class.getMethod( "exam", examMethodParams );
Object[] actualParams = { untypedTest };
examMethod.invoke( null, actualParams );
} catch (Exception e) {
e.printStackTrace();
}
}
}
答案 10 :(得分:0)
HI,
搜索完i后,发现无法在运行时进行动态类型转换。所以我想弄清楚的似乎是荒谬的。
我试图减少方法1的圈复杂度。我试图创建一个方法2,其中包含方法1中找到的重复模式的通用模式,并在必要时从方法1调用方法2 ...
第一种方法中的模式是这样的..
if (obj instanceof Cust)
{
Cust c = (Cust) obj;
Set cxSet = c.getCustPhonSet();
CustPhon cx;
if (cxSet != null && cxSet.size() > 0)
{
Iterator cxSetIterator = cxSet.iterator();
while (cxSetIterator.hasNext())
{
cx = (CustPhon) cxSetIterator.next();
this.stringProp(cx);
}
}
//....pattern continues here... CustPhon is replaced by various classes like CustNam etc... Also getCustPhonSet by getCustNamSet etc...
}
所以我想为上面的模式写一个通用的方法,比如这个::
public void dynamicIteration(Set xlSet, String clsName)
{
if (xSet != null && xSet.size() > 0)
{
try{
Class clsinstance = Class.forName(clsName);
Iterator itr = generalSet.iterator();
while(itr.hasNext())
{
this.stringProp(clsinstance.cast(itr.next()));// See this is wrong.. thats y i posted here by using a simple Horse example
}
}catch(ClassNotFoundException e)
{
e.printStackTrace();
}
}
}
从方法1调用方法2
//process customer email address
Set cxSet = c.getCustPhonSet();
className = "pkg.CustPhon";
dynamicIteration(cxSet,className);
// Similarly for other patterns
通过这种方式,我必须能够降低圈复杂度
这就是我想要做的事情。
答案 11 :(得分:0)
嘿,我认为这可以解决问题。你需要决定它是哪个对象,以便你可以调用相应的操作,对吧???
由于我们使用重写的命令,因此可以实现所需的功能。
Eddie给出的可能是最合适的解决方案吗?
你会覆盖相应类中的方法,以便在调用它时采用corressponding方法。
你知道了吗?