Reputation: 904
Suppose that I have interface MyInterface and 2 classes A, B which implement MyInterface.
I declared 2 objects: MyInterface a = new A()
, and MyInterface b = new B()
.
When I try to pass to a function - function doSomething(A a){}
I am getting an error.
This is my code:
public interface MyInterface {}
public class A implements MyInterface{}
public class B implements MyInterface{}
public class Tester {
public static void main(String[] args){
MyInterface a = new A();
MyInterface b = new B();
test(b);
}
public static void test(A a){
System.out.println("A");
}
public static void test(B b){
System.out.println("B");
}
}
My problem is that I am getting from some component interface which can be all sorts of classes and I need to write function for each class.
So one way is to get interface and to check which type is it. (instance of A)
I would like to know how others deal with this problem??
Thx
Upvotes: 4
Views: 806
Reputation: 49362
public interface MyInterface {}
public class A implements MyInterface{}
public class B implements MyInterface{}
public class Tester {
public static void main(String[] args){
MyInterface a = new A();
MyInterface b = new B();
test(b); // this is wrong
}
public static void test(A a){
System.out.println("A");
}
public static void test(B b){
System.out.println("B");
}
}
You are trying to pass an object referenced by MyInterface
reference variable to a method defined with an argument with its sub type like test(B b)
. Compiler complains here because the MyInterface
reference variable can reference any object which is a sub type of MyInterface
, but not necessarily an object of B
.There can be runtime errors if this is allowed in Java. Take an example which will make the concept clearer for you. I have modified your code for class B
and added a method.
public class B implements MyInterface {
public void onlyBCanInvokeThis() {}
}
Now just alter the test(B b)
method like below :
public static void test(B b){
b.onlyBCanInvokeThis();
System.out.println("B");
}
This code will blow up at runtime if allowed by compiler:
MyInterface a = new A();
// since a is of type A. invoking onlyBCanInvokeThis()
// inside test() method on a will throw exception.
test(a);
To prevent this, compiler disallows such method invocation techniques with super class reference.
I'm not sure what are you trying to achieve but it seems like you want to achieve runtime polymorphism. To achieve that you need to declare a method in your MyInterface
and implement it in each of the subclass. This way the call to the method will be resolved at run time based on the object type and not on the reference type.
public interface MyInterface {
public void test();
}
public class A implements MyInterface{
public void test() {
System.out.println("A");
}
}
public class B implements MyInterface{
public void test() {
System.out.println("B");
}
}
public class Tester {
public static void main(String[] args){
MyInterface a = new A();
MyInterface b = new B();
b.test(); // calls B's implementation of test()
}
}
Upvotes: 0
Reputation: 33
Use only one public class/interface in one .java file, otherwise it'll throw error. And call the object with the object name.. You declared two methos in Teater class only, then what the purpose of declaring class A,B.
Upvotes: 1
Reputation: 70201
Sounds like you need either a) to leverage polymorphism by putting method on MyInterface and implementing in A and B or b) some combination of Composite and Visitor design pattern. I'd start with a) and head towards b) when things get unwieldy.
My extensive thoughts on Visitor:
http://tech.puredanger.com/2007/07/16/visitor/
Upvotes: 0
Reputation: 11254
It sounds like you are after something like this:
public static void test(MyInterface obj){
if(obj instanceof A) {
A tmp = (A)obj;
} else if(obj instanceof B) {
B tmp = (B)obj;
} else {
//handle error condition
}
}
But please note this is very bad form and indicates something has gone seriously wrong in your design. If you don't have control of the interface then, as suggested by marcj, adding a second interface might be the way to go. Note you can do this whilst preserving binary compatibility.
Upvotes: 3
Reputation: 6846
I think visitor design pattern will help you out here. The basic idea is to have your classes (A and B) call the appropriate method themselves instead of you trying to decide which method to call. Being a C# guy I hope my Java works:
public interface Visitable {
void accept(Tester tester)
}
public interface MyInterface implements Visitable {
}
public class A implements MyInterface{
public void accept(Tester tester){
tester.test(this);
}
}
public class B implements MyInterface{
public void accept(Tester tester){
tester.test(this);
}
}
public class Tester {
public static void main(String[] args){
MyInterface a = new A();
MyInterface b = new B();
a.accept(this);
b.accept(this);
}
public void test(A a){
System.out.println("A");
}
public void test(B b){
System.out.println("B");
}
}
Upvotes: 1
Reputation: 134260
You have 3 options:
visit(Visitor)
where the Visitor
class contains lots of methods for visiting each subclass.if-else
inside your method test(MyInterface)
to check between themATester
, BTester
etc, all of which implement the interface ITester
which has the method test(MyInterface)
. Then in the ATester
, check that the type is equal to A before doing stuff. Then your main Tester class can have a chain of these testers and pass each MyInterface
instance down the chain, until it reaches an ITester
which can handle it. This is basically turning the if-else
block from 2 into separate classes.Personally I would go for 2 in most situations. Java lacks true object-orientation. Deal with it! Coming up with various ways around it usually just makes for difficult-to-follow code.
Upvotes: 0
Reputation: 1649
I'm not sure if I fully understand the issue, but it seems like one way might be to move the test() methods into the child classes:
public interface MyInterface {
public void test();
}
public class A implements MyInterface{
public void test() {
System.out.println("A");
}
}
public class B implements MyInterface{
public void test() {
System.out.println("B");
}
}
public class Tester {
public static void main(String[] args){
MyInterface a = new A();
MyInterface b = new B();
b.test();
}
}
You could similarly use a toString() method and print the result of that. I can't quite tell from the question, though, if your requirements make this impossible.
Upvotes: 1
Reputation: 17787
I usually use an abstract class to get around this problem, like so:
public abstract class Parent {}
public class A extends Parent {...}
public class B extends Parent {...}
That allows you to pass Parent objects to functions that take A or B.
Upvotes: 0
Reputation: 914
Can you not just have a method on the interface which each class implements? Or do you not have control of the interface?
This would provide both polymorphism and avoid the need to define any external methods. I believe this is the intention of an interface, it allows a client to treat all classes implementing it in a non type specific manner.
If you cannot add to the interface then you would be best introducing a second interface with the appropriate method. If you cannot edit either the interface or the classes then you need a method which has the interface as a parameter and then check for the concrete class. However this should be a last resort and rather subverts the use of the interface and ties the method to all the implementations.
Upvotes: 5
Reputation: 12509
I'm unclear on what you're actually asking, but the problem is that you don't have a method that takes a parameter of type MyInterface. I don't know what the exact syntax is in Java, but you could do something like if (b is B) { test(b as B) } but I wouldn't. If you need it to be generic, then use the MyInterface type as the variable type, otherwise use B as the variable type. You're defeating the purpose of using the interface.
Upvotes: 2