Reputation: 3415
Object aThing = new Integer(25);
The method call aThing.intValue()
is a compiler error. Why doesn't polymorphism work in this case?
Also there's a related statement in my textbook that is a bit convoluted:
The type of reference, not the type of the object referenced, determines that operations can be performed.
Can you briefly elaborate on that?
Where as Computer[] labComputers = new Computer[10];
works with polymorphism
public class Computer {
int ram;
public Computer(int rm){
ram= rm;
}
public String toString(){
String result = "ram is " + ram;
return result;
}
}
public class Notebook extends Computer{
int size;
public Notebook(int rm, int sz){
super(rm);
size = sz;
}
@Override
public String toString(){
String result = super.toString() + " size is " + size;
return result;
}
}
Added: I believe somewhere in the middle, there would be
labComputer[1] = new Notebook(2,15);
labComputer[2] = new Computer(2);
For the method call labComputers[1].toString()
, polymophism ensures that the correct toString
is called. In my mind labComputer[1] = new Notebook(2,15);
is equivalent to Object o = new Integer(25);
. But polymorphism worked for my Computer example not in the Object-Integer example. Why?
Upvotes: 0
Views: 324
Reputation: 63442
Because even though Integer
is an Object
, Object
is not an Integer
and therefore it doesn't have Integer
's methods, only Object
's.
The type of reference, not the type of the object referenced, determines that operations can be performed.
By that they mean that even though the object that is referenced contains more functionality, if the type of the reference is different from the type of the object, then only the functionality of they type of the reference will be available.
In the following:
Object aThing = new Integer(25);
the type of aThing
is declared as Object
. Even though the implementation contains more than that, whatever else the implementation contains is not visible anymore, because the type of the variable is not Integer
, but Object
. Both Object
and Integer
have methods in common, but you can only access the ones provided by Object
from now on, because nobody other than you know that this is really an Integer
, not just an Object
.
In the second example they mean that even though both Object
and Integer
have methods in common, when you call one of the methods, the method of the actual type will be called. So in this case, if you call toString()
on aThing
, you will call Integer
's toString()
method, not Object
's. Therefore this is only an access issue. The fact that you declare it as an Object
doesn't mean that you will get Object
's methods to respond to calls, it only means that whatever methods that are present in Integer
and not in Object
will just be unavailable.
Example:
class Computer {
public int getInstalledOperatingSystems() { return 3; }
}
class Desktop extends Computer {
public String getShapeOfCase() { ... }
}
class Notebook extends Computer {
public int getInstalledOperatingSystems() { return 1; }
public String getBrand() { ... }
public void closeLid() { ... }
}
Now let's create a Notebook
:
Notebook n = new Notebook();
Suppose that you have the following method:
public String showStatistics(Computer computer) {
result = "Operating systems: " + computer.getInstalledOperatingSystems();
return result;
}
If you call this method with the Notebook
you defined above:
showStatistics(n);
then the method will receive the Notebook
, because a Notebook
is a Computer
. It can call the Notebook
's getInstalledOperatingSystems()
method, because any Computer
has that method. When it calls it, it will receive 1
as a result, because Notebook
overrides Computer
's implementation. But showStatistics()
will not be able to call any other method that Notebook
provides, because it doesn't know that it's being passed a Notebook
. For all it cares, it has a Computer
, and it doesn't know of any other method that a Computer
doesn't have.
You can very well send it a Desktop
instead, or maybe tomorrow you will create a GamingComputer
that extends Desktop
or a Server
class that extends Computer
. You can send that as well. But showStatistics()
will not have access to any of Server
's specific, specialized methods, because showStatistics()
doesn't know that it's looking at a Server
. It wasn't even invented when showStatistics()
was written.
This is consistent with the statement that even though a Server
is always a Computer
, a Computer
is not always a Server
.
You can check though. So if you know that you might be passed in a Notebook
, not only a computer, you can look for that and you can even call Notebook
's methods, but you have to be sure that you're looking at a Notebook
:
if (computer instanceof Notebook) {
// this is definitely a Notebook, so you can assure the compiler
// of that explicitly, by using a cast
Notebook notebook = (Notebook) computer;
result += "This isn't just a generic computer, it's actually a notebook! ";
// now that you have a variable of type Notebook, you can call
// whatever Notebook provides
result += "It's produced by: " + notebook.getBrand();
}
Upvotes: 5
Reputation: 47729
The compiler needs to be told what type it should expect the object to be, so it can look up the class and see if the method is legit.
So you can say Object aThing = new Integer(25);
but then you'd want to say int thingValue = (Integer)aThing.intValue();
If you have a MySubClass object that is is a subclass of MyClass, and you want to call a method defined in MyClass (even if reimplemented in MySubClass) you could say:
Object myObject = new MySubClass();
int someValue = (MyClass)myObject.methodInMyObject();
Upvotes: 0
Reputation: 85779
It won't work because your variable is from Object
class, so you can only use methods in the Object
class. If you want to use it as an Integer
, you should first do a (down) type casting:
Integer aInteger = (Integer)aThing;
//it could maybe work
aInteger.intValue();
Now, why could maybe work? Because downcasting could throw a ClassCastException if the type casting won't work.
Based in your example, I would post a basic code to show how polymophism works:
class Animal {
public void move() {
System.out.println("I'm an animal and I can move.");
}
}
class Cat extends Animal {
//this means a Cat would change the move behavior from the Animal instance
@Override
public void move() {
System.out.println("I'm a cat and I can move.");
}
}
class Dog extends Animal {
//this means a Cat would change the move behavior from the Animal instance
@Override
public void move() {
System.out.println("I'm a dog and I like to run.");
}
public void bark() {
System.out.println("I can bark!");
}
}
public class AnimalTest {
public static void main(String[] args) {
//it will take the behavior of the Animal class reference
Animal animal = new Animal();
//it will take the behavior of the Cat class reference
Animal cat = new Cat();
//it will take the behavior of the Dog class reference
Animal dog = new Dog();
//this will invoke the Animal#move method
animal.move();
//this will invoke the Cat#move method because it was overriden in the Cat class
cat.move();
//this will invoke the Dog#move method because it was overriden in the Dog class
dog.move();
//this line won't compile if uncommented because not all animals can bark.
//dog.bark();
//if you want to make the dog barks, then you should use the downcasting
((Dog)dog).bark();
//note that this will only work for Dog class reference, not for any other class
//uncomment this line and the code will compile but throw a ClassCastException
//((Dog)cat).bark();
}
}
Upvotes: 5
Reputation: 2917
Think of it this way, a dog is an object and a dog class might have methods such as
dog.bark()
If you cast Dog up the hierarchy to an Object, it becomes less specific.
Object o = dog;
Now all you know is that o is an object, you do not know what kind of objects and therefore you cannot know if this object can bark.
When you move UP the hierarchy you almost always lose functionality by being less specific about what you have.
Upvotes: 2