I just started to learn Java, so please bear with me if the answer is somewhat obvious. I did some research but no avail.
From what I understand, attributes are not overriden but only field hidden. To determine whether the attribute in the superclass or the subclass is used, Java will check the type of the reference.
Then I don't under stand the output here:
public class Super {
String str = "I'm super!\n";
public String toString() {
return str;
}
}
public class Sub extends Super {
String str = "I'm sub.\n";
}
public class TestFH {
public static void main(String[] args) {
Sub s1 = new Sub();
System.out.printf(s1.toString());
}
}
It gives me:
I'm super!
I understand that I can achieve what I want easily via method overriding. I'm just curious about what's happenning under the hood.
Thanks in advance.
When you call, s1.toString(), it's finding toString() method defined only in Super class hence using that method as super class methods are available in the sub class. Your super class method toString() is using it's own class variable str (with value initialized in super class) as the return value from the method and hence the behavior i.e. output as I'm super!.
If you want to get the output as I'm sub.\n then you need to reuse the same variable as in the super class and assign the new string value i.e. I'm sub.\n to it. Best option is to use constructors as:
public class Super {
String str = "I'm super!\n";
public Super(String stringValue){
this.str = stringValue;
}
public String toString() {
return str;
}
}
public class Sub extends Super {
public Sub(){
super("I'm sub.\n");
}
}
public class TestFH {
public static void main(String[] args) {
Sub s1 = new Sub();
System.out.printf(s1.toString());
}
}
You're hiding (shadowing) str in your child class. Since you have not overridden toString() in your child class, the call is being made in the parent class, and it sees the parent's str.
If you did this:
public class Sub extends Super {
public Sub() {
this.str = "I'm sub.\n";
}
}
It would output what you're expecting.
This call is using the super class:
Sub s1 = new Sub();
System.out.printf(s1.toString());
The reason is that Sub is not overriding str, it is just declaring another variable that happens to have the same name. In other words, sub is just hiding the variable in Super (data members are not polymorphic).
You could give them different names if they mean different things. Or maybe have the Sub access (or modify) the parent's attribute using a getter method.
you're not assigning the string literal, "I'm sub.\n" to the shared superclass field, you're creating a field local to the subclass and assigning it to that instead.
for example,
public class EncapsulationDemo {
public static void main(String[] args){
MySuperObject obj = new MySubObject();
System.out.println(obj); // prints I'm sub.
}
private static class MySuperObject{
String str = "I'm super."; // protected, can be accessed directly
// by subclasses
#Override
public String toString(){
return str;
}
}
private static class MySubObject extends MySuperObject{
MySubObject(){
super();
str = "I'm sub."; // assign to superclass field
}
}
}
for more information, please see Controlling Access to Members of a Class.
Related
I am using bounded type parameters in generic (JAVA).
class First<T extends Use>{
T s;
First(T s){
this.s=s;
}
void setS(T s){
this.s=s;
}
void getS(){
System.out.println(s);
}
}
class UseChild extends Use{
public String toString(){
return "I am UseChild";
}
}
class Use{
public String toString(){
return "I am Use";
}
public static void main(String[] args){
First <Use> f1 = new First <Use> (new Use());
f1.getS();
f1.setS(new UseChild());
f1.getS();
}
}
Output is:
I am Use
I am UseChild
Till now its ok. I thought I can pass child class object to setS(), because "T extends Use" is there in Type-parameter, otherwise I could not.
But Output is same if I am using type-parameter without extends keyword as:
class First<T>
{
//Same code as above
}
My doubt is:
What is special about extends keyword if I can pass child class object to setS() without extends keyword?
Let's say Use defines a new method (instead of overriding toString):
class Use {
...
public void printMe() { // Some nonsensical method for demonstration
System.out.println("printing Use");
}
}
This bound would allow you to call this method from the First class:
class First<T extends Use> {
T s;
...
public void getS() {
s.printMe(); // not possible without the bound.
}
}
The way you're using First, you don't need generics at all. Using the type Use instead of T would work the same:
class First {
Use s;
First(Use s) {
this.s = s;
}
void setS(Use s) {
this.s = s;
}
void getS() {
System.out.println(s);
}
}
The output would be the same.
The <T extends Use> here acts as a restriction. It specifies that you can ONLY pass objects of type Use or that extend type Use.
Changing to <T> is equivalent to <T extends Object> - in other words, you will be able to pass any object.
The difference is that with First<T> you could pass any object, for example a LinkedList or a JFrame. With First<T extends Use> you can only pass objects of type Use or that extend type Use.
One way this can be very useful is if type Use has certain methods that you want your class First to use. In that case you will need to specify <T extends Use> so that First will be aware of the methods and properties of the Use class.
The Type-parameter(T) of any generic class accepts type-argument and children of type argument. We can pass object of UseChild in setS(), even if object of class First is created using Use as type-argument. Because UseChild is child class of Use. So following statements are perfectly valid together:
First<Use>f = new First<Use>(new Use());
f.setS(newUseChild());
Difference between bounded and unbounded types are not as it stated in the question. Actual difference between is discuss below:
If we do not use bounded type, then T extends Object, and s becomes reference variable of object type.
class Use
{
public String toString()
{
return "I am Use";
}
public void call()
{
System.out.println("I am call of Use");
}
public static void main(String[] args)
{
First <Use> f1 = new First <Use> (new Use());
f1.getS();
}
}
Lets check various fields of class First:
import java.lang.reflect.*;
class First<T>
{
T s;
First(T s)
{
this.s=s;
}
void setS(T s)
{
this.s=s;
}
void getS()
{
Field[] flds = this.getClass().getDeclaredFields();
for(Field f:flds)
{
String name = f.getName();
String type = f.getType().getName();
int i = f.getModifiers();
String modifier = Modifier.toString(i);
System.out.println("Name = "+name+" type= "+type);
}
System.out.println(s.getClass().getName());
}
}
Output is:
Name = s type= java.lang.Object
Use
So T extends java.lang.Object. s is reference variable of type java.lang.Object. We cannot call Use specific methods using variable s.
Lets check various fields of class First With bounded type:
import java.lang.reflect.*;
class First<T extends Use>
{
T s;
First(T s)
{
this.s=s;
}
void setS(T s)
{
this.s=s;
}
void getS()
{
Field[] flds = this.getClass().getDeclaredFields();
for(Field f:flds)
{
String name = f.getName();
String type = f.getType().getName();
int i = f.getModifiers();
String modifier = Modifier.toString(i);
System.out.println("Name = "+name+" type= "+type);
}
System.out.println(s.getClass().getName());
}
}
Output is:
Name = s type= Use
Use
So now T extends Use, instead of java.lang.Object. s is reference variable of type Use. As said by #jorn vernee now we can call Use specific methods from First.
Isn't super suppose to refer object of Object class type which is not created here?
public class SuperChk
{
void test()
{
System.out.println(super.toString());
}
public static void main(String[] args)
{
SuperChk sc1 = new SuperChk();
System.out.println(sc1);
sc1.test();
}
}
Isn't super suppose to refer object of Object class type which is not created here?
Err ... no.
The super keyword (when used that way) says "want to refer to this, but viewed as an instance of its superclass". It is the way that a subclass calls methods in a superclass that it may have overridden.
The java.lang.Object class is the ultimate superclass of all reference types, and your SuperChk class is no different. Your SuperChk class has toString() method (inherited from Object) and super.toString() is calling it.
Now in your example, super. is redundant because SuperChk does not override String. But here is an example where it is NOT redundant ...
public class SuperChk {
private void test() {
System.out.println(toString());
System.out.println(super.toString());
}
#Override
public String toString() {
return "Hello world";
}
public static void main(String[] args) {
SuperChk sc1 = new SuperChk();
sc1.test();
}
}
If you compile and run that, you will see that toString() and super.toString() are calling different methods.
Every Java class inherits implicitly from Object. So you call the method toString defined by Object and inherited by your class.
This code prints "GenericAnimal", where as I was expecting it to print "PolymorphismTest" as I created an object of PolymorphismTest.
class GenericAnimal{
String name="GenericAnimal";
}
public class PolymorphismTest extends GenericAnimal {
String name = "PolymorphismTest";
public static void main(String[] args) {
GenericAnimal animal = new PolymorphismTest();
System.out.println(animal.name);
}
}
It's called field "hiding" or "shadowing". You have a second field of the same name as the field in the parent class. If the one in the parent were private then it would not be accessible to the subclass.
The extra String name field in the subclass occupies its own memory and reference. You probably should re-use the field of the parent class by either making it visible (i.e. protected or public scope) or adding a protected or public accessor and mutator to the parent class that the subclass can invoke to access and manipulate the field.
Because you're accessing the field, rather that a function. The second field shadows the one in the superclass, rather than overriding. This is not the same as the behaviour of functions, which is probably what you were looking for here:
class GenericAnimal {
String name = "GenericAnimal";
public String getAnimal(){
return name;
}
}
public class PolymorphismTest extends GenericAnimal {
String name = "PolymorphismTest";
public static void main(String[] args) {
GenericAnimal animal = new PolymorphismTest();
System.out.println(animal.name);
System.out.println(animal.getAnimal());
PolymorphismTest testAnimal = (PolymorphismTest)animal;
System.out.println(testAnimal.name);
System.out.println(testAnimal.getAnimal());
}
public String getAnimal(){
return name;
}
}
Your GenericAnimal reference cannot reference to the property of its subclass PolymorphismTest. Property values cannot be overridden in java, only the methods.
Note:
This is NOT about hiding at all. (That would be the other direction of reference.)
I don't know what this way of passing an object as a parameter is called. So I didn't know what to search for on the site. My apologies if this has been asked before, which I'm sure it has.
Can somebody tell me what the difference is between these two ways of passing on object as a parameter? And why can't I override properties as well as methods in the first way?
myObject.doSomethingWithOtherObject((new OtherObject() {
// why can't I override properties here?
public String toString () {
return "I am the other object.";
}
...
}));
myOtherObject = new OtherObject();
myObject.doSomethingWithOtherObject(myOtherObject);
Then I have a second question, but maybe answering the first question will also answer this one.
Why does this not work:
public class OtherObject {
public OtherObject (String str) {
super();
}
}
myObject.doSomethingWithOtherObject((new OtherObject("string arg") {
public void overrideSomething () {
}
}));
The string "string arg" is not passed to the constructor as I would have expected. Instead in complains about not finding the constructor OtherObject().
Why does Java not recognize I'm trying to send an argument to the constructor?
Thanks for reading!
when you do new OtherObject and then open curly brackets { ... you are creating a new (annonymous) class. not a new Object of the already defined OtherObject class. have a look at Java's annonymous classes to better understand
The first one is called an anonymous class. It means that you actually instantiate a subclass without a name, in order to override something.
In the second one, the compiler complains that you do not have a constructor. As described in the answer to your first question, you are actually subclassing here, meaning you have to define a constructor if you need one.
Your code:
myObject.doSomethingWithOtherObject((new OtherObject() {
// why can't I override properties here?
public String toString () {
return "I am the other object.";
}
...
}));
Is roughly equivalent to:
class OtherSubObject extends OtherObject {
public String toString () {
return "I am the other object.";
}
}
...
myObject.doSomethingWithOtherObject(new OtherSubObject());
except that the subclass of OtherObject has been created without a name (i.e. is anonymous) and so cannot be referred to anywhere else.
Your code:
myOtherObject = new OtherObject();
myObject.doSomethingWithOtherObject(myOtherObject);
does not create a new class, it just passes an instance of OtherObject as the parameter.
First of all I'd like to confirm what my previous speakers said:
When you add curly brackets to the Object initialization you'll create an anonymous subtype of the class for which you can override methods as you did it in your example.
But now to your comment-question: why can't I override properties here?
You can: by adding another pair of curly brackets, which will create a constructor for the anonymous type, e.g.:
public class OtherObject {
protected String name;
public OtherObject(String name) {
super();
this.name = name;
}
public String toString() {
return getClass() + ": " + name;
}
public static void main(String[] args) {
OtherObject o1 = new OtherObject("bar");
OtherObject o2 = new OtherObject("bar") { // will call parent constructor first -> name = "bar"
{
// constructor for the anonymous type
name = "foo"; // overwrite the name
}
};
System.out.println(o1); // prints "class OtherObject: bar"
System.out.println(o2); // prints "class OtherObject$1: foo"
}
}
Calling a method with a new object which is followed by curly braces is like extending that class and it is called anonymous class in java.
myObject.doSomethingWithOtherObject(new OtherObject() {
public String toString () {
return "I am the other object.";
}
});
The reason you cannot override properties is probably because of their access modifiers.
For overriding a class member it should have access modifier of at least protected.
public class OtherObject {
protected String name;
}
You can now override name in your anonymous class.
I have a super class "Sam" and a sub-class "SubSam"
public class Sam {
String msg;
String msg1;
Sam(String mm, String mm1) {
msg = mm;
msg1 = mm1;
}
#Override
public String toString() {
return this.msg + " " + this.msg1;
}
}
class SubSam extends Sam {
String msg1="C";
public static void main(String[] args) throws Exception {
SubSam obj = new SubSam();
System.out.println(obj);
}
SubSam() {
super("A", "B");
}
}
The output is:
A B
Why "toString()" is referring the instance fields of "Sam" instead of "SubSam". The output should be: A C
I am thinking over it for a long time now, but not getting?
Because instance variables in Java aren't overridden, quite simply. A subclass can define a variable with the same name as one defined in one of its superclasses, but it counts as a separate variable for all intents and purposes.
For example, consider the following code:
public class A {
public String var;
}
public class B extends A {
public int var;
}
Given those definitions, instances of B will have two variables, one being of type String, the other of type int, but both will be named var. They are still separate variables which can be independently assigned to and read from, and Java does not consider there being anything wrong with this.
If you want to override behavior as you indicate you want, you need to use methods instead. For instance, you could do it like this:
public class A {
public String msg;
private String msg1;
public A(String mm, String mm1) {
this.msg = mm;
this.msg1 = mm1;
}
public String msg1() {
return(this.msg1);
}
#Override
public String toString() {
return(this.msg + " " + msg1());
}
}
public class B extends A {
public B() {
super("A", "B");
}
#Override
public String msg1() {
return("C");
}
}
Now, if you call System.out.println(new B());, it will print A C.
You're not overriding toString, so the toString method of the superclass is called. It prints msg and msg1 for the superclass, not for the subclass.
You are setting the msg1 in the sub-class, but in it's contructor you are passing
super("A","B");
At this point the variable that are being initialized in the super i.e. the contructor of main class are the main class' variable that are being used using the this keyword in toString method.