Reputation: 33
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);
}
}
Here the argument for the exam function is an Object
. But I want to have the argument be Horse
... so what changes must be done in "exam(hrt.cast(hritr.next()))
" method call? I don't want to explicitly use the class name Horse
in callHorse()
... So what am I supposed to do?
Thanks
Upvotes: 0
Views: 6775
Reputation:
Hey i think this would solve the problem. You need to decide upon which object it is, so that u can call the corresponding operation, right???
Since we use the overriding comcepts the very functionality that s required can be achieved.
What is given by Eddie may be the most appropriate solution for this i guess?
YOu override the method in the respective classes so that on calling it goes the corressponding method.
U got it?
Upvotes: 0
Reputation: 54431
Note: Code with sequences of "if (x instanceof MyClass)
usually indicates that you are not using polymorphism enough. Code can usually be refactored to get rid of the need to test this. But I'll ignore this for the sake of answering the question asked.
You can do what you are trying to do, but not without some code changes. Method overloading cannot do what you need because in Java, method overloading is decided at compile time. Thus, if you have two methods in a class where both methods have the same name, same return type, but different parameter types, then any code invoking this overloaded method must make explicit which one will be invoked. Your current code does this with the types it provides due to the use of explicit casts but the fully dynamic version does not. If method overloading were decided at runtime, then your code would do what you want. But because it is decided at compile time, your code does not compile.
To solve your problem, you can use generics, or you can restructure your code. First I'll introduce a test harness that shows a very simplified version of what you're starting with:
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 {}
This code runs and does what you would expect. It prints "Neigh" followed by "Moo".
You are trying to replace
if (obj instanceof Horse) {
Horse c = (Horse) obj;
noise(c);
}
with
if (obj instanceof Horse) {
handleNoise(obj, Horse.class);
}
and then adding the method to handle it (simplified):
void handleNoise(Object obj, Class clazz) {
noise(clazz.cast(obj));
}
and as I said before, this doesn't work the overloading of noise
is decided at compile time. The compiler sees that you are casting, but does not know at compile time what the type is. So it cannot pick an overloading and compilation fails.
The best way to solve this is by using polymorphism, because polymorphism is decided at runtime. That is, have all of those classes implement some interface and then move the code in question into the individual classes. Here is an example that does this:
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");
}
}
Notice how much simpler the test method is! If you can have each item implement an interface that handles what you call stringProp below, then you can simplify part way:
if (obj instanceof Cust) {
loopOverSet(c.getCustPhonSet());
} else if (obj instanceof Name) {
loopOverSet(c.getCustNameSet());
}
// and so on for the rest...
and then add the method:
void loopOVerSet(Set cxSet) {
if (cxSet != null && cxSet.size() > 0) {
Iterator cxSetIterator = cxSet.iterator();
while (cxSetIterator.hasNext())
{
((StringProp)cxSetIterator.next()).stringProp();
}
}
}
This assumes that the previously-overloaded methods stringProp
have been moved into the individual classes CustPhone
and CustName
and so on and that these classes all implement some interface which I've called StringProp
where this interface defines the method stringProp()
. Since this code is using overriding instead of overloading it will be decided at runtime.
Upvotes: 3
Reputation: 33
HI,
After searching through i found that dynamic typecast at runtime can't be performed. So what i was trying to figure out seems to be absurd.
I was trying to reduce the cyclomatic complexity of a method1. I was trying to create a method2 which contains the generalized pattern of the repetition pattern found in method1 and calling the method2 from method1 wherever necessary...
the pattern was like this in the first method..
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...
}
so i thought of writing a generalized method for the above pattern like this::
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();
}
}
}
Calling method2 from method 1
//process customer email address
Set cxSet = c.getCustPhonSet();
className = "pkg.CustPhon";
dynamicIteration(cxSet,className);
// Similarly for other patterns
By this way i must be able to reduce the cyclomatic complexity
This is what i was trying to do..
Upvotes: 0
Reputation: 48131
Is your real goal to have multiple versions of the exam() method, that take different types as parameters, and dynamically select the version needed at runtime?
You can do this explicitly with reflection. Here's an example program.
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();
}
}
}
Upvotes: 0
Reputation: 73655
Why not write it like this? What exactly are your requirements?
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);
}
Depending on what you do in the exam() method, it might also make sense to make it an instance method of Horse, like this:
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);
}
...
}
Upvotes: 0
Reputation: 54431
If you are doing a dynamic cast by using Class.cast()
with an argument that you're passing to another function, then at compile time nothing is known about the type that you are passing. This is why you cannot use Horse
as the argument type where you define the method, but then call the method using reflection in the way that you are. Your cast does very little except verify that -- as long as you don't get an Exception
-- the set you pass in is entirely comprised of members of the Class
you pass in the name of.
Note that the Class.cast()
method was introduced in Java 5, meaning you have access to Generics if you have access to Class.cast()
. Generics can help clean things up although they won't solve the problem you are trying to solve.
Using a Java 5 for
loop you can rewrite your loop as follows:
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();
}
}
This version is less cluttered and makes your cast more obvious. You are casting to an entirely arbitrary type. The cast may be to any type as long as the class definition can be loaded from the classpath. Thus, if your exam()
method takes an argument of Horse
then the compiler knows that it cannot guarantee that the call will succeed and the code fails to compile.
Even if you try overloading, it won't work. That is, if you create the methods:
public static void exam(Object obj) {
System.out.println("Object " + obj);
}
public static void exam(Horse obj) {
System.out.println("Horse " + obj);
}
the exam(Object)
method will always be the one invoked. Try it.
The bottom line is that what you're trying to do cannot be done. You need to give us more information about exactly what your goal is before we can help you.
Upvotes: 0
Reputation: 12296
When you say:
exam(Horse hrr)
you're telling the compiler that you want it to check all calls to exam() and make sure that each call provides a Horse object as an argument. However, in callHorse(), you're invoking exam() with a dynamically cast argument, and the compiler has no way to check the argument.
It's possible that you could work around this by using reflection and dynamically invoking the exam() method.
Upvotes: 1
Reputation: 7288
I'm not sure it's possible or desirable to avoid having a reference to "Horse" in the callHorse method. Judging from the printstacktrace after a ClassNotFoundException, you throw a hard error if the class is not found for some reason.
Couldn't you, for the same reason, just cast to "Horse" and then catch the classcastexception if something in the Set is not a Horse?
Can you explain why it exactly is that you need to pass in the classname instead of the class?
Maybe you can also use method overloading, but I'd have to test this, because I'm not entirely sure what the precedence is in this case.
Upvotes: 0
Reputation: 21620
It looks like your design is wrong for Java, and you can't directly do what you are asking for.
Perhaps you need to reshape your code to use the visitor pattern? Failing that, you need to explain your requirement instead of the solution that you want to use. In that way, we can tell you the proper Java solutions to your requirement.
Upvotes: 0
Reputation: 7952
First things first,your set should be using either generics or explicitly defined as only holding Horse Objects.
(final Set xSet<Horse>, final String clsName){
...}
Fix that and you have fixed 90% of the issues.
Upvotes: 0
Reputation: 57333
You could explicitly cast in the function call -
try {
Class hrt = Class.forName(clsName);
Iterator hritr = xSet.iterator();
while(hritr.hasNext()){
exam((Horse)hrt.cast(hritr.next()));
}
}
but I'm not really sure what you're trying to achieve here - If you're writing code that explicitly references Horses, why do you need to dynamically determine the class type from a string?
Upvotes: 0
Reputation: 83993
You might want to take a look at generics.
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);
}
Of course in your example you could always just cast the objects. Why you don’t want to do that is beyond me.
Upvotes: 1