Reputation: 2071
I would like to perform several operations based on the type of an object and without using instanceof
. At first I was thinking of overloading methods based on types (as seen below), and thought that maybe Java would choose the method appropriately (based on most specific class of object).
import java.util.ArrayList;
import java.util.List;
public class TestA {
public static void main(String[] args)
{
List<Object> list = new ArrayList();
list.add(new A());
list.add(new B());
list.add(new C());
list.add(new Object());
TestA tester = new TestA();
for(Object o: list)
{
tester.print(o);
}
}
private void print(A o)
{
System.out.println("A");
}
private void print(B o)
{
System.out.println("B");
}
private void print(C o)
{
System.out.println("C");
}
private void print(Object o)
{
System.out.println("Object");
}
}
class A {
}
class B extends A {
}
class C {
}
The output is:
Object
Object
Object
Object
However the output I'm after is:
A
B
C
Object
instanceof
visitor.accept(this)
in class A
causes the function visitor.accept(A o)
be called.instanceof
because I've read using it is bad practice; in this case, would it still be bad practice?Upvotes: 8
Views: 2552
Reputation: 1380
Well my answer would be to make classes A,B and C implement a common interface.
And then each of them can have their own specific implementations of this interface.
This way, you can call the same method for all the objects(thus avoiding overloaded methods), and also ensure custom functionality based on the type of the object(i.e the class from which it was instantiated).
Upvotes: 11
Reputation: 25873
This answer considers that modifying given classes and their relationship is not an option.
Is there a way to make Java choose the method based on the most specific type of the parameter object?
Java compiler cannot cast this for you because maybe that's not what you want (in your case that's what you want, but maybe other people don't want this behavior and they would be no solution for them). Since you're passing an Object
reference to print()
, the compiler will call print(Object)
.
If not, what alternatives can I look at for such functionality, without the aid of instanceof
You can wrap your Object
with its type in a wrapper class, e.g.:
public class ObjectWrapper {
private final Object object;
private final int type;
}
This way you can safely cast to the type of the Object
without using instanceof
. Although this IMHO is more complicated than simply using instanceof
, and actually it only creates your own instanceof
...
I'm honestly against instanceof because I've read using it is bad practice; in this case, would it still be bad practice?
In programming, nothing is always a bad practice. Everything depends on the situation. In this situation I think you can use instanceof
and do unsafe casting because the design requires so. And anyway, if instanceof
operator exists in the language, is because it's used. If instanceof
was always a bad practice, it wouldn't exist as part of the language. See this and this.
I was actually trying to simulate the visitor pattern however it seems, what makes visitor pattern work is because of the double dispatch, which makes the parameter being "accepted" be in the correct type during function call, particularly, visitor.accept(this) in class A causes the function visitor.accept(A o) be called.
Check my second link for cons on using the visitor pattern.
Upvotes: 5
Reputation: 24443
I would just use instanceof
here. It's simple and clear.
It's not like there is a hard rule to not use instanceof
. There are no hard rules :) Lots of use of instanceof
may be a sign that you could change things to make the compiler do more work for you. Whether that is actually worth doing needs to be looked at case by case. In your case you mention you aren't able to change the classes in question, so it's not even an option.
Upvotes: 1
Reputation: 1327
From my point of view it's not the question whether or not use instanceof. The question is how to minimise the impact gained by this situation. So I would recommend instance adapter here in case there are several operations to deal with. That way you can at least avoid spreading that complexity all over the system:
public abstract class Adapter {
public abstract void print();
public static Adapter wrap(Object o) {
if (o instanceof A) {
return new AAdapter();
}
if (o instanceof B) {
return new BAdapter();
}
return new ObjectAdapter():
}
public class AAdapter {
public void print() {
System.out.printLn("A");
}
}
Then loop:
for (Object o: things) {
Adapter.wrap(o).print();
}
Of course our Adapter will have a reference to the wrapped object as the method name implies. That way you get a coding model close to direct modification of those classes.
Upvotes: 0
Reputation: 1630
What pops out of my mind without use instanceof now is:
Code:
public interface YourInterface {
public void doStuff();
}
public class A implements YourInterface {
@Override
public doStuff() {
System.out.print("A");
}
}
public class B implements YourInterface {
@Override
public doStuff() {
System.out.print("A");
}
}
List<YourInterface> list = new ArrayList<YourInterface>();
list.add(new A());
list.add(new B());
for(YourInterface e: list)
{
e.doStuff();
}
Code:
public enum YourEnum {
TYPE1 {
@Override
public void doStuff() {
System.out.print("Type 1");
},
TYPE2 {
@Override
public void doStuff() {
System.out.print("Type 2");
};
public abstract void doStuff();
}
List<YourEnum> list = new ArrayList<YourEnum>();
list.add(YourEnum.TYPE1);
list.add(YourEnum.TYPE2);
for(YourEnum e: list)
{
e.doStuff();
}
Upvotes: 0
Reputation: 9755
After reading above answer
One solution which i can suggest would be to use check the object type by using o.getClass
and continue
However many better solutions are posted. I totally agree with what @krisna kishor shetty said
Upvotes: 0
Reputation: 136012
Consider Visitor design pattern http://en.wikipedia.org/wiki/Visitor_pattern. Though it will need changes to A,B,C otherwise there is no other way.
Upvotes: 2
Reputation: 1250
package com.ank.says;
import java.util.ArrayList;
import java.util.List;
class Base{
public void print(){
System.out.println("Base");
}
}
class A extends Base{
@Override
public void print() {
System.out.println("A");
}
}
class B extends Base{
@Override
public void print(){
System.out.println("B");
}
}
class C extends Base{
@Override
public void print(){
System.out.println("C");
}
}
public class TestPlayGround {
public static void main(String[] args) throws ParseException {
List<Base> list = new ArrayList<Base>();
list.add(new A());
list.add(new B());
list.add(new C());
list.add(new Base());
TestPlayGround tester = new TestPlayGround();
for(Base o: list)
{
o.print();
}
}
}
Output : - A B C Base
Upvotes: 0
Reputation: 1095
Method Overloading is a compile time polymorphism, in your for
loop , you have declared o
as Object
and hence always print(Object o) will be called.
Solution: Use Dynamic Polymorphism:
class A{
void print(){
System.out.println('in A');
}
}
class B{
void print(){
System.out.println('in B');
}
}
and the for loop
for(Object o: list){
o.print();
}
Upvotes: 3