Page 1 of 1
Java inheritance
Posted: Mon May 03, 2010 3:19 pm
by Bullet Pulse
I know this is basic stuff, but because I want some clarification on this, I'm asking you guys.
I have three classes:
Code: Select all
public class BalletDancer extends Dancer
{
public void glide() { }
}
Code: Select all
public class Dancer
{
public void leap() { }
}
Code: Select all
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.
Am I correct?
Or are there more ways?
Re: Java inheritance
Posted: Mon May 03, 2010 3:44 pm
by avansc
Not sure what you are trying to do, but this is how i learnt it.
(see what happens when you make glide static)
Code: Select all
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;
}
Code: Select all
public class bird extends animal {
public bird(int hp) {
super(hp);
System.out.println("Creating Bird\n");
// TODO Auto-generated constructor stub
}
public static void fly()
{
System.out.println("Flying");
}
}
Code: Select all
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");
}
}
Re: Java inheritance
Posted: Mon May 03, 2010 4:04 pm
by Falco Girgis
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.
Re: Java inheritance
Posted: Mon May 03, 2010 4:45 pm
by Arce
It is also worth noting that in java, all methods not declared with the keyword "final" are virtual...
Re: Java inheritance
Posted: Tue May 04, 2010 9:12 am
by christo
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.
Re: Java inheritance
Posted: Tue May 04, 2010 10:16 am
by Falco Girgis
^
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...)
Re: Java inheritance
Posted: Tue May 04, 2010 11:48 am
by thejahooli
In C# I think you can use the 'is' keyword so you can know which type it is.
Code: Select all
if(dancerObject is BalletDancer)
{
// Code Here
}
Maybe there's something like this in Java.
Re: Java inheritance
Posted: Tue May 04, 2010 11:55 am
by Falco Girgis
thejahooli wrote:In C# I think you can use the 'is' keyword so you can know which type it is.
Code: Select all
if(dancerObject is BalletDancer)
{
// Code Here
}
Maybe there's something like this in Java.
There is also the "as" keyword which is a safe typecast. If the parent reference cannot be cast to a child, the reference is set equal to null.
It is a "safer" approach in C#, but rethinking your OO design is probably the best approach.
Re: Java inheritance
Posted: Tue May 04, 2010 1:31 pm
by Bullet Pulse
There is also the instanceOf keyword
Re: Java inheritance
Posted: Tue May 04, 2010 1:40 pm
by Falco Girgis
Bullet Pulse wrote:There is also the instanceOf keyword
I'm speaking C# and you're speaking Java. instanceOf is "is" in C#.
Re: Java inheritance
Posted: Tue May 04, 2010 5:53 pm
by christo
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.
Re: Java inheritance
Posted: Wed May 05, 2010 10:28 am
by Skullman
Hey christo more posts like these when these subjects crop up in Java or C# please. Very informative and interesting. Thanks.