public class Test
{
public static void main(String[]args)
{
Dancer d = new BalletDancer(); //"Slices" the BalletDancer part of the object.
BalletDancer b = new BalletDancer();
b.leap();
b.glide();
//d.glide(); would cause an error
d.leap();
}
}
d.glide() can't be called because the assignment of Dancer d = new BalletDancer() removes the BalletDancer members from d right?
To avoid this, you would either:
(a) Add a glide function to the Dancer class, or
(b) Cast d to a BalletDancer object.
public class animal
{
public animal(int _hp)
{
System.out.println("Animal Created.\n");
this.hp = _hp;
}
public int get_HP()
{
return this.hp;
}
public void set_HP(int _hp)
{
this.hp = _hp;
}
private int hp;
}
public class test
{
/**
* @param args
*/
public static void main(String[] args)
{
// TODO Auto-generated method stub
animal ani = new animal(10);
animal dove = new bird(5);
bird.fly();
System.out.println("start");
}
}
Some person, "I have a black belt in karate"
Dad, "Yea well I have a fan belt in street fighting"
Bulletpulse, you're correct. The proper solution is to cast the reference back down the inheritance tree to the derived class.
Even though it is instantiated as a derived class, you are referring to it through a reference to the parent class--and that method is not implemented at the parent level.
I don't think casting is good form. It means you have a design problem and you are fumbling your type information. Of course it may get you over the line.
Java has static type checking which means that if you declare your dancer to be of type Dancer (which you do) then you can only call methods declared on the Dancer type. The methods are not removed they are there but the compiler will not let you call them because it deliberately enforces the declared type. You know they are there because you can call them if you cast. You can also see that they are there using reflection.
So it's the declared type which is important to the compiler and not the actual implementation.
So why is casting no good?
If you want to call glide() it implies you know you have a BalletDancer. If you find yourself getting only a Dancer somewhere in your code even though you know you have a BalletDancer, you have lost type information somewhere along the line. The parts of your program that can make do with a Dancer should accept a Dancer and the declared type they deal with can be that. But the parts of your program that need to call glide() should declare their need of a BalletDancer. Try not to lose type information in middle men (going from BalletDancer -> Dancer -> BalletDancer (via a cast). Because one day your cast will fail because you are not getting help from the compiler to ensure that you have an implementation of glide(), you're only finding out at runtime if you have a bug of that kind. And that bug will only be discovered if that cast happens to be executed. This might only be on completion of the final quest in your game!!
The introduction of Generics in Java 5 was supposed to help you keep this type information. The case of collections is the most well known because if you have a plain old List it loses all the type info and you can only get Objects out whereas if you have a List<BalletDancer> you don't need to cast to BalletDancer. Sometimes you want to use a List<? extends Dancer> and then concretely declare a type to be a List<BalletDancer> which is a specialisation of a List<? extends Dancer>.
Generics are tricky but they can usually help you keep the type information you create when you make these classes. Let the compiler save you from your future self and avoid downcasting.
I agree. Even in C++ I think that having to cast downwards is generally a result of poor design. (though without some of the OO mechanisms that Java and .NET offer, sometimes it is unavoidable...)
instanceof is like casting, it should be avoided and usually can be unless there is a bigger design problem you can't solve now (think legacy code).
First, just to be clear, when I say "type" I mean "class or interface or primitive" in java.
The basic reasoning here is that type information can be checked by the compiler and this is a whole category of bugs you can avoid because the compiler can stop you from creating them. instanceof and casting leave you open to these bugs because they are runtime type checks and conversions. They only blow up if you go down that code path.
Inheritance is cool when you learn it but you should think of it as only one tool in the language for creating a design that matches the domain you're trying to create with the program.
Java (and c# which is essentially the same language with minor differences) allows you to use interface inheritence in a more obvious way that c++. Interfaces (in c++ this would be classes consisting only of virtual functions) permit multiple inheritance but it's inheritance of the "interface" or contract and not the implementation. For key classes and declared dependencies between classes, interfaces should be preferred over concrete types (interfaces use "interface" where concrete classes use "class" in the source code). The reason for this is that the implementation can be changed at runtime or for testing purposes etc. and the loose coupling that results is much easier to modify if you need to make a new design.
For example perhaps you need to put in a FastBalletDancer which has crazy optimised code in it, so you can do this later if you made an interface for BalletDancer and originally implemented this interface with a class called BalletDancerImpl or SimpleBalletDancer or something.
Tradiional OO teaching has seemingly equated Object-Oriented => Inheritance whereas there are plenty of languages both before and after c++ which have no implementation inheritance (e.g. Smalltalk and Javascript use prototype inheritance which is totally different). Implementation inheritance can be hard to manage and hard to comprehend and easy to get into a mess.
Of course in a small program (i.e. < 10kLOC) many of these things are fine because you generally have all the code and you remember writing it. If you work on big systems or have the luxury problem of shipping software that becomes wildly successful and therefore grows into a big system, you find that inheritance hierarchies are usually unscalable (they quickly becomes too big to manage).
I know this is more that what you were asking about but I really hate seeing things like ifs or switch blocks of instanceof in code because it is basically failing to use the type system to model the decision to switch to an implementation and instead falling back on an imperative (if this then or if that then or else the other), explicit and unextensible decision path that should require no executable code (had it been written correctly).
It's bloat. Flab. Lard.
If you are using instanceof consider putting the result of the decision code into the class you're casting to. Then you can remove the switch and instanceof block. And just call the method that does the right thing for that type. Less code is the primary metric.
The only line of code that can't have a bug on it is the one you never write.