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.
Related
This question already has an answer here:
Scope and use of super keyword in Java
(1 answer)
Closed 5 years ago.
class Feline {
public String type = "f ";
public Feline() {
System.out.print("feline ");
}
}
public class Cougar extends Feline {
public Cougar() {
System.out.print("cougar ");
}
void go() {
type = "c ";
System.out.print(this.type + super.type);
}
public static void main(String[] args) {
new Cougar().go();
}
}
In this code output is coming as feline cougar c c and when I am changing subclass variable as String Type = "c" means assigning new String type then answer is coming as feline cougar f f please let me know how this and super keyword is working in this subclass method?
type is an unqualified name, and refers to a local variable, parameter, or field.
this.type refers to a field accessible to the current class.
super.type refers to a field accessible to the base class.
Since the subclass Cougar does not have a field named type, both this.type and super.type refers to the type field declared in base class Feline. In your example, there is no difference between this and super.
The statement type = "c "; in method go() is unqualified, and since there is no local variable or parameter by that name, it also refers to field type of base class Feline. As such, type, this.type, and super.type all refer to the one and only field named type.
If the statement in method go() is changed to String Type = "c";, then it defines a differently named local variable. Remember, Java names are case-sensitive, so Type and type are not the same name. So, field type retains the initialized value of "f ".
If you intended to change the statement in method go() to String type = "c";, then it defines and initialize a local variable named type. By nature, it cannot update field type, since the initializer applies to the newly declared local variable. So, field type retains the initialized value of "f ".
If you first declare the local variable in method go() using String type;, then assign it like you original code does using type = "c";, then the unqualified name refers to the local variable, not the field, by that name. The local variable is hiding the field of the same name. So, once again, field type retains the initialized value of "f ".
Now I see that . Your Cougar class is missing "type" variable. I have checked in my code and this.type and super.type are the same variable. Setting type = "c" is equal to this.type = "c" and super.type="c" because they point same field.
Edited: Write something like this
class Cougar extends Feline {
public String type = "c ";
....
}
Here is what JavaDoc says:
public final Class <?> getClass()
Returns the runtime class of this Object. The returned Class object is the object that is locked by static synchronized methods of the represented class.
The actual result type is Class<? extends |X|> where |X| is the erasure of the static type of the expression on which getClass is called. For example, no cast is required in this code fragment:
Number n = 0;
Class<? extends Number> c = n.getClass();
Returns:
The Class object that represents the runtime class of this object.
Now , I understand it is a native method , so it is is implemented in platform-dependent code. But what about the return type of this method.
public final Class<?> getClass()
Also , consider the code:
class Dog
{
#Override
public String toString()
{
return "cat";
}
}
public class Main
{
public static void main(String[] args)
{
Dog d= new Dog();
//Class<Dog> dd = new Dog(); Compile time error
System.out.println(d.getClass());
}
}
Output:
class Dog
So, my query lies in :
Return type of this method
toString method is not called . A similar post on this topic is :
Java. getClass() returns a class, how come I can get a string too?
The commented code which otherwise give compile time error.
The data for each object contains a reference to an object of class java.lang.Class, and this is returned by the method getClass. There is also one java.lang.Class object describing java.lang.Class.
Think of a Class object as the "blueprint" describing a certain class from which objects are being made. It stands to reason that blueprints also need a blueprint of their own (or else how would engineers know how to make blueprints).
These statements try to illustrate this.
Integer integer = 1;
Class<?> clazzInteger = integer.getClass();
System.out.println( "class of integer=" + clazzInteger );
Class<?> clazzClazzInteger = clazzInteger.getClass();
System.out.println( "class of class Integer's class=" + clazzClazzInteger );
String string = "x";
Class<?> clazzString = string.getClass();
System.out.println( "class of string=" + clazzString );
Class<?> clazzClazzString = clazzString.getClass();
System.out.println( "class of class String's class=" + clazzClazzString );
Output:
class of integer=class java.lang.Integer
class of class Integer's class=class java.lang.Class
class of string=class java.lang.String
class of class String's class=class java.lang.Class
A class has a name, just like anything described by a blueprint has a name which is not to be confused with the blueprint itself. If a class object appears in a certain context, its toString() method is called implicitly, and this returns the class' name. If you'd like to print all the nitty-gritty details of a class (akin to printing the blueprint itself) you'd have to write a lot of code - just look at the javadoc for java.lang.Class: there's an awful lot of information to be retrieved (as befits a blueprint).
At this point, we need to differentiate between a type and an instance of the type. Lets explain it with an example.
public class A {
public static void main(String[] args) {
Class<A> typeInformation = A.class; //Type information associated with type `A`
A instanceOfA = new A(); //actual instance of type `A`
}
}
Type
The reference 'typeInformation' in the above code is of the type Class keeping aside the generics for a while. This information will typically be residing in non-heap memory section. Following information is store against each of the type jvm loads :
The fully qualified name of the type
The fully qualified name of the type's direct superclass (unless the type is an interface or class java.lang.Object, neither of which have a superclass)
Whether or not the type is a class or an interface
The type's modifiers ( some subset of` public, abstract, final)
An ordered list of the fully qualified names of any direct superinterfaces
Instance
instaneOfA is a reference to the actual instance of the type A which points to an address in the heap memory.
Return type of getClass() is a generic Class type. Like many other types available in java - String, Integer etc, Class is also a type representing the type information associated.
toString() method is associated and invoked on an instance of the Dog class, not on the Dog type itself.
//Class<Dog> dd = new Dog(); Compile time error
This is due to Type mismatch occuring while assigning the result of expression in the right hand side to the Left Hand Side, which is not of the same type.
Class dd refers to a reference of Class type.
Dog is a different type altogether, and a new Dog() can be assigned to a reference of the type 'Dog'.
This link will help you understand the design aspects of java runtime environment
I have an answer for your Question 3,
This gives compile time error because
Reason 1: For a Class instance, You can only assign class object that represents the Dog class, but you can't assign the Dog class object directly.
For example: Class dd=Dog.class or Class dd=Class.forName("Dog");
is correct syntax.
Reason 2: The class Class is a final class but not a super class for Dog class. You go back to the concept of dynamic method dispatch in java,where you can only assign subclass objects to a superclass variable.
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();
Say, there is a following example:
class Super {
public int i = 3;
public void m(Object o) {
System.out.println("Object " + i);
}
public void m(String o) {
System.out.println("String " + i);
}
}
public class Sub extends Super {
public Sub() {
i = 5;
}
public static void main(String[] args) {
Super s = new Sub();
Object o = "";
s.m(o);
s.m("");
}
}
The result of this code is:
Object 5
String 5
But I thought it would be:
String 5
String 5
Don't quotes set String as this object's type? There are definitely some cases of casting to String with a help of quotes, so I'm a little confused about this basic example. Thanks in advance.
The type of the method is determined in compile time, and not in run time. The dynamic dispatch exists only for the "parameter" this - there is no dynamic dispatch for parameters in static typing languages such as java.
The compiler "choses" which method should be invoked, and since o is of type Object - it choses m(Object) - it has no way to know that the dynamic type of o is actually a String.
If you are interested - a common way to overcome this issue in some cases is using the visitor design pattern.
In your specific case, in order to "force" the activation of m(String) you should use m(o.toString())
In sb.m(o), you're calling m() with an Object reference, so Java chooses that overload. Java will not choose a more specific overload than the reference type you pass it. It will go up the inheritance chain though. Say you didn't have m(String o), calling sb.m("Hello") would still be legal, but it would call the object version.
If you were to do sb.m((String) o), you would get your expected behavior.
You declared the object as Object so its type is Object. Types in Java are strong and static so when you declare an object as type Type that is what its type will be for life.
If you want it to be a string you'll have to use a toString method or a cast (String)o
You are downcasting the String to an Object. What you are doing is similar to this.
public class Sub extends Super {
public Sub() {
i = 5;
}
public static void main(String[] args) {
Super s = new Sub();
Object o = "";
System.out.println("Object Type = " + o.getClass().getName());
s.m(o);
s.m((Object)"");
}
}
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