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?
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();
}
}
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();
}
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.
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();
Related
what I have is a list of Dog objects, in the objects there contains a value that is a Boolean to show whether the dog as completed a training or not. What im trying to do is iterate over the list and only return Dog() objects that have completed training, for instance if their are 12 dogs, and only 3 have completed training, the loop should only print those objects.
else if (input == 1) {
for (int i = 0; i < 12; ++i) {
//Create a temporary value to hold the object.
Object tempHold = dogKennel.getAnimal(i);
//If animal has not graduated, skip, else print.
if (!(tempHold.getGraduation())) {
continue;
}
else {
System.out.println(dogKennel);
}
}
getAnimal(i) returns the object at int i
the method .getGraduation is defined and does return a Boolean however the compiler doesnt want to recognize temp value as is, and doesnt go beyond that value. the compiler keeps suggesting to cast tempHold, but even if I do, it doesnt work.
i feel like it would work if i could get it to compile, as the object that is returned would have a getGraduation() method (it is defined for the super class of the animal.)
however the compiler doesn't want to recognize temp value as is, ...the compiler keeps suggesting to cast tempHold, but even if I do, it doesn't work.
The compiler is telling you two things:
An Object is NOT a Dog (the opposite is true. A Dog is an Object).
class Object does not have method isGraduated() defined for them.
To fix this, you can cast Object to Dog:
Dog tempHold = (Dog)dogKennel.getAnimal(i);
Now that we have a Dog, we can safely invoke isGraduated() on it. But the problem is we canNOT be sure that we have a Dog. We may as well have a Cat if we get the Animal from another kennel. In that case, you will get a ClassCastException which tells you that Cats cannot be cast as Dogs.
To avoid getting run time exceptions, you can add a check:
Object tempHold = dogKennel.getAnimal(i);
if(tempHold instanceof Dog) {
Dog dog = (Dog)tempHold;
System.out.println(dog.isGraduated());
}
The instanceof check fixes the problem.
There are ways to avoid this run time check altogether. One would be to create an interface:
public interface CanGraduate {
default boolean isGraduated() {
return false;
};
}
Then make all Animals implement this interface:
public abstract class Animal implements CanGraduate {
//Behavior common among all animals
}
You can now freely add new animal types and be assured that you can safely invoke isGraduated() on them and get a false value as long as they inherit from the above Animal class.
For dogs, isGraduated() is supposed to be more meaningful. So you can override it in their case:
public class Dog extends Animal {
private boolean _graduated = true;
#Override
public boolean isGraduated() {
return _graduated; //or some complex logic that determines graduation
}
}
With this structure, you no longer need to worry about invoking the method on any kind of Animal.
As an example, let us see some driver code:
public class Main {
public static void main(String[] args) {
Dog dog1 = new Dog();
Dog dog2 = new Dog();
Cat cat1 = new Cat();
Cat cat2 = new Cat();
List<Animal> dogKennel = List.of(dog1, dog2);
List<Animal> catKennel = List.of(cat1, cat2);
for(Animal x : catKennel) {
System.out.println(x.isGraduated());
}
}
}
The program will simply output false since Cats can never graduate. If the kennel contained Dogs, it would output the actual graduation status of the dog.
Like the others have said, the getGraduation() method is only defined presumably in the Dog class. This means that the method can only be called on Objects with the type Dog. To define a variable with type Dog you can do Dog temphold = *whatever*. The reason it wants you to cast is because Object is a supertype of Dog. If you'd like to read more about casting you can here: https://javarevisited.blogspot.com/2012/12/what-is-type-casting-in-java-class-interface-example.html
Basically, all you have to do is a cast to convert the object
if (!(((Dog)tempHold).getGraduation()))
{
continue;
}
this casting tells the compiler that even though tempHold is an object of the Object class it also is an object of the Dog class and should have all of its properties
If you want to filter the list for just ones with a certain condition, the most common way to do that these days is with the Stream::filter method.
It would look like this:
List<Dog> completedTraining =
dogKennel
.stream()
.filter(
dog -> !dog.getGraduation()
)
.collect(
Collectors.toList()
)
;
My included code's output is pretty ugly, but it's just a code for understanding how different things may work in Java. The questioned line is marked with comments on the bottom half of the code.
class CsTorta extends Torta{
public CsTorta retegez(CsTorta a){
....
}
public CsTorta retegez(Torta a){
System.out.println("This method"); //<-----it calls this one and not the one above
....
}
}
public class NewClass {
public static void main(String[] args) {
Torta tt=new Torta(5);
Torta tcs=new CsTorta(3);
CsTorta cs=new CsTorta(4);
System.out.println("");
System.out.println(tcs.retegez(tcs)); //The line in question uses the cstorta retegez method (marked with "This method")
}
}
While the tcs's type in coding-time is the reference type, in runtime when i call the tcs.retegez method it recognizes its a cstorta type, but the parameter which is the same tcs remains the reference type (thats why it uses the cstorta marked method).
My question is: Is my conclusion correct: that the program only checks the "real" type of the object if it calls a method, and uses the reference type if it does not?
That's pretty much correct. What is needed here is understanding the difference between overloading and overriding.
Overriding occurs when you have a class that declares an instance method, and a subclass that declares the same method (same name, same parameters--the result type is usually the same but could be a subclass). There are multiple methods to choose from, but the exact method is determined at run time.
public class A {
public void method1(String s1, int s2) { ... }
}
public class B extends A {
#Override
public void method1(String s1, int s2) { ... }
}
A object = new B();
object.method1("xxx",2);
The decision about which method1 is run isn't made until run time. object's real type is B, so the method1 declared in B is called.
Overloading is when two methods with the same name, but different parameters, are both present. By different parameters, I mean that the number of parameters is different, or the number of parameters is the same but the types are different. That is, they have different signatures. In that case, the decision on which method to call is made at compile time. (You can have a case where both overriding and overloading occur. The decision about which parameter signature to choose is made at compile time; but if there are multiple overriding methods with the same signature, the choice between those methods is made at run time.)
The key thing to remember is that if the decision is made at compile time, the compiler will not know what the "real" type of the object is. All it knows is how you've declared it. Thus:
public CsTorta retegez(CsTorta a){ // Number 1
....
}
public CsTorta retegez(Torta a){ // Number 2
System.out.println("This method"); //<-----it calls this one and not the one above
....
}
These are overloaded methods.
Your code looks like:
Torta tcs = // the compiler doesn't care
System.out.println(tcs.retegez(tcs));
The compiler has to decide whether to call Number 1 or Number 2. All the compiler knows is that the parameter is a Torta. The actual value could be an object of Torta, CsTorta, or any other class. Or it could be null (all right, you can't call tcs.retegez if it's null, but if you said tcs.retegez(tcs2), then tcs2 could be null.) The compiler doesn't know, and doesn't care. All it knows is that it was declared as a Torta, so it chooses the overloaded method with the Torta parameter.
(To clarify further: the compiler will choose the deepest subclass it can. Example:)
class AnotherTorta extends Torta { ... }
class YetAnotherTorta extends CsTorta { ... }
AnotherTorta t3 = // whatever
YetAnotherTorta t4 = // whatever
tcs.retegez(t3);
// since AnotherTorta can't be cast to CsTorta, it chooses the Torta parameter
tcs.retegez(t4);
// here, t4 can be cast to either a Torta or CsTorta parameter, so it chooses the subclass, CsTorta
public class Maryland extends State { Maryland() { /* null constructor */ }
public void printMe() { System.out.println("Read it."); }
public static void main(String[] args) {
Region mid = new State();
State md = new Maryland();
Object obj = new Place();
Place usa = new Region();
md.printMe();
mid.printMe();
((Place) obj).printMe();
obj = md;
((Maryland) obj).printMe();
obj = usa;
((Place) obj).printMe();
usa = md;
((Place) usa).printMe();
}
}
class State extends Region {
State() { /* null constructor */ }
public void printMe() { System.out.println("Ship it."); }
}
class Region extends Place {
Region() { /* null constructor */ }
public void printMe() { System.out.println("Box it."); }
}
class Place extends Object {
Place() { /* null constructor */ }
public void printMe() { System.out.println("Buy it."); }
}
Hi There.
I'm trying to understand the behaviour of the above main method which prints the following output when run.
Read it.
Ship it.
Buy it.
Read it.
Box it.
Read it.
I'm particularly struggling to understand the output of the last two printMe methods in the main function.
My understanding is that the first two print me operations will use there super classes printMe method as the objects have not been explicitly downcast to the sub class and thus are considered to be State and Region objects respectively by the Java compiler.
I also believe I understand the next two outputs in which the classes are downcast and thus the subclass printMe functions will override the superclass functions.
However I am struggling to understand what is occurring in the last two printMe methods. I can see that the variable obj is initially declared as an Object and then downcast as a Place is assigned a reference to the usa object(of type Place). So why is the output then of type region in this instance? I feel I am missing fundamental here in my understanding.
In Java, instance method calls follow inheritance. No matter what the reference type is, it will call the method of the type of the actual object.
The type of the reference only determine which methods the compiler knows you can call. e.g.
String hi = "Hello";
Object A = hi;
String hiToString = A.toString(); // calls String.toString();
int l = A.length(); // won't compile even though the String in A has a length() method.
Note: for static methods, the instance is ignored and it is the compile time type which determines which method is called.
In Java, non-static methods are called with late-binding, which means we cannot make sure which function it will call until run-time.
My understanding is that the first two print me operations will use there super classes printMe method as the objects have not been explicitly downcast to the sub class and thus are considered to be State and Region objects respectively by the Java compiler.
Explicitly downcast does nothing in this code. Downcast is actually performed.
Object md is still a reference to State and Object mid is a reference of Region. Compiler will never know which printMe() will be called. It only checks that State and Region classes have the function printMe() or not.
At run-time, when the md.printMe() is called, JVM will check the Runtime type information (RTTI) of md, and know it is a Object of State class. Therefore, the printMe() function in the State class is called, no matter what md is declared. (Of course, you need to override the printMe() in the State class. If you don't, State class will inherit the printMe() function from its superclass Place, and the printMe() function in Place class will be called. You can check this by removing the printMe() function in your State class.)
According to these rules, the output is reasonable.
The type cast in ((Place) obj).printMe() is needed just because in Object class, there's no printMe() function in it. Still, the compiler can't make sure which function ((Place) obj).printMe() will called, it is decided at run-time. For example, if you change your ((Maryland) obj).printMe(); to ((Place) obj).printMe();, the output is still the same.
For static methods, these rules will not fit. You can read more information about them with the keywords "Overloading, Overriding and Hiding". These terminologies will help you to understand the inheritance system in Java.
I'm learning for my java certification and I came across this piece of code.
class Feline {
public String type = "f ";
public Feline() {
System.out.print("feline ");
}
}
public class Cougar extends Feline {
public Cougar() {
System.out.print("cougar ");
}
public static void main(String[] args) {
new Cougar().go();
}
void go() {
type = "c ";
System.out.print(this.type + super.type);
}
}
And when I run it, I get "feline cougar c c " so I get why it returns feline and cougar after it but why super.type refers to a Cougar object and not a Feline Object?
I saw this post but it didn't really enlightened me.
super.type is just referring to the same variable as this.type... there's only one object involved, and therefore one field.
When you create an instance of a subclass, it doesn't create two separate objects, one for the superclass and one for the subclass - it creates a single object which can be viewed as either the superclass or the subclass. It has a single set of fields. In this case, you have a single field (type) which originally had a value of "f ", but whose value was then changed to "c ".
There is just one type variable. Your Cougar's go() method sets it to "c ".
Therefore both this.type and super.type print c.
this-> invokes current class :Cougar
super-> invokes Feline
Feline is super class of Cougar because Cougar inherited from Feline. If you want to use Feline class fields in Cougar, You should use super.
You can see: http://www.instanceofjava.com/2015/03/this-vs-super-keywords.html
I would like to add one more thing here for completeness
public Cougar() {
System.out.print("cougar ");
}
This constructor is translated by the compiler like this
public Cougar() {
super(); // Added by compiler if not added explicitly by programmer
System.out.print("cougar ");
}
Thats why you get feline first and then cougar in output.
Other than that,there is only one type variable involved as explained in other answers, so it is printing c for both of them.
Consider this:
class A {
int x =5;
}
class B extends A{
int x =6;
}
public class CovariantTest {
public A getObject() {
return new A();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
CovariantTest c1 = new SubCovariantTest();
System.out.println(c1.getObject().x);
}
}
class SubCovariantTest extends CovariantTest {
public B getObject(){
return new B();
}
}
As far as I know, the JVM chooses a method based on the true type of its object. Here the true type is SubCovariantTest, which has defined an overriding method getObject.
The program prints 5, instead of 6. Why?
The method is indeed chosen by the runtime type of the object. What is not chosen by the runtime type is the integer field x. Two copies of x exist for the B object, one for A.x and one for B.x. You are statically choosing the field from A class, as the compile-time type of the object returned by getObject is A. This fact can be verified by adding a method to A and B:
class A {
public String print() {
return "A";
}
}
class B extends A {
public String print() {
return "B";
}
}
and changing the test expression to:
System.out.println(c1.getObject().print());
Unless I'm mistaken, methods are virtual in java by default, so you're overriding the method properly. Fields however (like 'x') are not virtual and can't be overriden. When you declare "int x" in B, you are actually creating a totally new variable.
Polymorphism doesn't go into effect for fields, so when you try and retrieve x on an object casted to type A, you will get 5, if the object is casted to type B, you will get 6.
When fields in super and subclasses have the same names it is referred to as "hiding". Besides the problems mentioned in the question and answer there are other aspects which may give rise to subtle problems:
From http://java.sun.com/docs/books/tutorial/java/IandI/hidevariables.html
Within a class, a field that has the
same name as a field in the superclass
hides the superclass's field, even if
their types are different. Within the
subclass, the field in the superclass
cannot be referenced by its simple
name. Instead, the field must be
accessed through super, which is
covered in the next section. Generally
speaking, we don't recommend hiding
fields as it makes code difficult to
read.
Some compilers will warn against hiding variables