views:

306

answers:

9

Hey everyone,

I am trying to wrap my mind around something in java. When I pass an object to another class' method, can I not just call any methods inherent to that object class?

What is the reason code such as the example below does not compile?

Thank you,

class a {
  public static void myMethod(Object myObj) {
    myObj.testing();
  }
}


class b {
  public void testing() {
    System.out.println ("TESTING!!!");
  }
}


class c {  
  public static void main (String[] args) {
    b myB = new b();    
    a.myMethod(myB);  
  }
}

Edit: The reason I have left the parameter in myMethod as type Object, is because I would like to be able to pass in a variety of object types, each having a testing() method.

+8  A: 

The problem is that myMethod can't know it's getting a b object until it actually runs. You could pass a String in, for all it knows.

Change it to

public static void myMethod(b myObj) {
  myObj.testing();
}

and it should work.


Update of the question:

Edit: The reason I have left the parameter in myMethod as type Object, is because I would like to be able to pass in a variety of object types, each having a testing() method.

As Amanda S and several others have said, this is a perfect case for an interface. The way to do this is to create an interface which defines the testing() method and change myMethod to take objects implementing that interface.

An alternative solution (without interfaces) would be to reflectively discover if the object has a testing() method and call it, but this is not recommended and not needed for a such a simple case.

Michael Myers
Ahh its a bit clearer now. I have updated the question, but what if I want to pass in an a, or a c, with each of those objects having their own testing() method?
barfoon
Then you should declare it to take a parameter of a common super class. The problem is that java.lang.Object does not have a testing method.
Aaron Maenpaa
Heh, I leave for a little bit and come back to find that the question is changed and there is a better answer for the new question. Oh well.
Michael Myers
@mmyers: Probably it would be better to understand java's type system first. Although I write that my self too. :) Cheers
OscarRyz
+2  A: 

Because you're passing in an Object (b inherit from Object). Object doesn't have testing, b does.

You can either pass in b or cast the object to b before calling the method.

EDIT To pass in a generic class that implements that method: you'll want to make an interface that has the method signature and pass in the interface type instead of Object. All objects that you pass in must implement the interface.

colithium
+1  A: 

You can only access the members that are visible for the type of reference you have to the object.

In the case of myMethod(Object myObj) that means only the members defined in Object, so in class a the members of class b will not be visible.

If you changed the definition of a.myMethod to be public static void myMethod(b myObj) you would then be able to see the testing method on the instance of b while in myMethod.

update based on clarification:

In that case defining an interface for all of them to implement is likely what you want.

public interface Testable {
    public void testing();
}

public class a {
    public static void myMethod(Testable myObj) {
        myObj.testing();
    }
}

public class b implements Testable {
    public void testing () {
        System.out.println("TESTING!!!");
    }
}
Jeremy Wilde
+16  A: 

If you would like to pass in a variety of objects with testing() methods, have each object implement a Testable interface:

public interface Testable
{
   public void testing()
}

Then have myMethod() take a Testable.

public static void myMethod(Testable testable)
{
  testable.testing();
}


Edit: To clarify, implementing an interface means that the class is guaranteed to have the method, but the method can do whatever it wants. So I could have two classes whose testing() methods do different things.

public class AClass implements Testable
{
   public void testing()
   {
      System.out.println("Hello world");
   }
}

public class BClass implements Testable
{
   public void testing()
   {
      System.out.println("Hello underworld");
   }
}
Amanda S
Each testing() method for each class of object will be different. So its not possible to have one generic testing method.
barfoon
By 'different', do you mean different signatures, or just different in what it does? If just the implementation is different, this will still work.
Rob Hruska
Different in what it does. The 'tests' that are applied to that object type will be different inside of each class.testing() method.
barfoon
@barfoon - Actually if want to pass in "a variety of testing objects" this is the best approach. You will have one expected method that EVERY class has to implement. i.e. When your code is expecting a Testable object you are guaranteed that it will have a testing() method.
KG
Do the classes HAVE to be public?
barfoon
No, that just makes them accessible to all other classes. Currently you have them set package-protected, which means they can't be accessed by classes outside their own package.
Amanda S
+5  A: 

What you are talking about is duck typing. Java doesn't have duck typing.

Therefore you need to define an interface that all the classes with a testing() method implement.

e.g:

public interface Testable
{
   public void testing()
}

class B implements Testable 
{
  public void testing() {
    System.out.println ("TESTING!!!");
  }
}

class A {
  public static void myMethod(Testable myObj) {
    myObj.testing();
  }
}
Douglas Leeder
+4  A: 

Your issue is a classic argument in favor of an interface. You want as generic as possible, yet you want every object you pass to have a testing() method. I suggest something along the lines of the following:

public interface Testable
{
  public void testing();
}

public class A
{
  public static void myMethod(Testable myObj)
  {    
    myObj.testing();
  }
}

public class B implements Testable
{
  public void testing()
  {
    System.out.println("This is class B");
  }
}

public class C implements Testable
{
  public void testing()
  {
    System.out.println("This is class C");
  }
}

public class Test
{  
  public static void main (String[] args) 
  {
    B myB = new B();
    C myC = new C();
    A.myMethod(myB); // "This is class B"
    A.myMethod(myC); // "This is class C" 
  }
}
Randolpho
This is very helpful, thank you.
barfoon
A: 

The object you pass in has to have a method of testing(). There is no method testing() in java.lang.Object. Either your objects implement Testable at in the other answer or you have to use reflection to check for a testing method.

class a {
  public static void myMethod(Object myObj) {
    try {
     Method m = myObj.getClass().getMethod("testing",(Class[])null);
     m.invoke(myObj, (Object[])null);
   } catch ( NoSuchMethodException e ) {
    // your choice
   } catch ( LotsOfOtherExceptions oe ) {}

  }
}


class b {
  public void testing() {
    System.out.println ("TESTING!!!");
  }
}


class c {  
  public static void main (String[] args) {
    b myB = new b();    
    a.myMethod(myB);  
  }
}
Clint
+1  A: 

Why can’t java find my method?

Because of the way Java was designed.

Java is "statically typed" that means objects types are checked during compilation.

In Java you can invoke a method only if that method belongs to that type.

Since this verification is made during compilation and the Object type does not have the "testing()" method, the compilation fails ( even though if at runtime the objects do have that method". This is primarily for safety.

The workaround as described by others will require you to create a new type, where you can tell the compiler

"Hey, the instances of this type will respond the the testing method"

If you want to pass a variety of objects and keep it very generic, one way is having those objects to implement and interface.

public interface Testable { 
     public void testing();
}

class A implements Testable { // here this class commits to respond to "testing" message 
     public void testing(){
     }
}

class B implements Testable { // B "is" testable 
    pubic void testing() { 
        System.out.println("Testing from b");
    } 
 }


class C implements Testable { // C is... etc. 
   public void testing() { 
          //.... 
    }
}

Later somewhere else

public void doTest( Testable object ) { 
        object.testing();
 }

doTest( new A() );
doTest( new B() );
doTest( new C() );

The "OTHER" way to do this, in java is invoking the methods reflectively, but I'm not sure if that's what you need, for the code is much more abstract when you do it that way, but thats how automated testing frameworks ( an a lot of other frameworks such as Hibernate ) do actually work.

I hope this help you to clarify the reason.

OscarRyz
A: 

If you REALLY, REALLY want to keep the parameter as abstract as possible, you should consider reflection API. That way, you can pass whatever object you want and dynamically execute the method you want. You can take a look at some examples.

It's not the only way, but it might be a valid alternative depending on your problem.

Keep in mind that reflection is way slower than calling your methods directly. You might consider using an interface as well, such as the one on Amanda's post.

Pablo Santa Cruz
Quiza primero deba de entender como funcionan las cosas en Java, y luego saltar a cosas más avanzadas. :) Saludos!
OscarRyz