views:

1297

answers:

13
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

+1  A: 

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.

Bombe
A: 

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?

Steve B.
A: 

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.

WolfmanDragon
A: 

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.

Darron
code i wrote was to explain what i wanted..Anyway i want to type cast the output of a set.. But the classname used to typecast is got dynamically i.e (clsname)hritr.next() instead of (Horse)hritr.next().. where clsname="Horse".. I think u got my point now... I cant use generic in Set...
msher420
No, I don't get your point yet. Why is the class name dynamic? Do these classes have a common base class? Are all the possible class names known at compile time?
Darron
phew!! it seems that we cant dynamically typecast an object.. anyway do see the last post i added.. Sorry if i am blunt.. an very amateur trying to equip myself... thanks
msher420
You can dynamically cast it...but why? You can't really do anything with it at that point unless you have a baseclass. Unless you do everything with reflection, in which case you don't need to cast it at all. I really thing you're trying to solve your problem the "dynamic language" way.
Darron
A: 

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.

Rolf
+1  A: 

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.

jdigital
A: 

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.

Eddie
code i wrote was to explain what i wanted..Anyway i want to type cast the output of a set.. But the classname used to typecast is got dynamically i.e (clsname)hritr.next() instead of (Horse)hritr.next().. where clsname="Horse".. I think u got my point now... I cant use generic in Set...
msher420
@msher420: *Why* do you want to cast the output of a set? What is the purpose of this cast? Without knowing this, it is difficult to help you.
Eddie
phew!! it seems that we cant dynamically typecast an object.. anyway do see the last post i added.. Sorry if i am blunt.. an very amateur trying to equip myself... thanks...
msher420
A: 

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);
    }
    ...
}
Esko Luontola
I almost wrote a response exactly like what you posted, but I think part of the requirement must involve using reflection.
Eddie
A: 

Hey Thanks for the replies, its the first time i am posting and got lot of help from u ppl... i love this forum!!

The code i have written is just for an example to show want i want..

I will tell my need here::

I want to type cast an Object type( from the Set) to my own class type i.e (Horse)hritr.next().. but i dont want to use the class name 'Horse' explicitly in the type cast but to use the String name instead i.e clsName in this example(where clsName = "Horse")...

Can i do it... thats what i was trying to achieve in the example.. but in vain

Please do suggest anyother way...

Thanks Again...

You already said that and we answered the question. If none of the answers are adequate, then tell us WHY you need to do it this way.
jdigital
A: 

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();
}

}

}
Dave Costa
A: 

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..

msher420
+1  A: 

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.

Eddie
A: 

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?