September 23, 2002

// INHERIT0
// The following code is an example of a hierarchy
// that classes might form.

// Here are the classes:
class A {}
class B extends A{}
class C extends A{}
class D extends B{}

// Here is the Class Hierarchy:
//
// A
// / \
// B C
// |
// D
//
// Note how you write superclasses above subclasses.
// A superclass may have multiple subclasses,
// but each subclass may have only ONE superclass.

public class inherit0 {
public static void main(String[] args) {
}
}


// INHERIT1

class Fruit {
int k=1;
void print() {
System.out.println(k);
}
}

class Apple extends Fruit {}
// What does $extends$ do? $Apple$ is a subclass of $Fruit$. Thus,
// $Apple$ inherits the instance variable ($k$) and instance method
// ($print$) from the superclass ($Fruit$)

// So, you don't need to repeat any code. Ain't life sweet? ;-)

// Actually, I'm taking some shortcuts here, because I've ignored
// constructors (using the defaults) and encapsulation (using "default
// visibility"). You DO need to worry about these things!

public class inherit1 {
public static void main(String[] args) {

Apple A = new Apple(); // create an $Apple$ object
A.print(); // access the $print$ method from $Fruit$

}
}

/* Output:
1
*/

// INHERIT2: See INHERIT1 for notes.

// What's different here? I've added a constructor to $Fruit$.
// In INHERIT1 I used used the default constructor $Fruit() {}$ to
// create $Fruit$ objects.

// Java requires that SUBCLASSES call the constructor of the
// superclass, using a method called $super$. Technically, $super$ is
// a special kind of method that calls a constructor of the
// superclass. Sometimes we even say "the $super$ constructor."

// + Java ALWAYS calls the $super$ constructor when you create a subclass.
// + A call to $super$ with or without arguments MUST be the first line
// in the constructor of your subclass.
// + If you do not call $super$, Java automatically calls $super()$
// + so, you must have an "empty" constructor in the superclass.
// + if you use the default constructor, then you get the "empty"
// constructor
// + if you provide constructors in the superclass, Java does not
// provide the default constructor, so YOU must provide it!
// + If you do call $super$, the number of arguments that you use must
// match the number of arguments in the superclass constructor.

class Fruit {
int k=1;
Fruit() {
print();
}
void print() {
System.out.println(k);
}
}

class Apple extends Fruit {}
// Java does not see a constructor for $Apple$.
// So, Java uses the default constructor $Apple() {}$ which
// AUTOMATICALLY calls $super()$ by default. The call to $super()$
// calls the superclass constructor $Fruit()$ which contains code that
// calls $print$ inside $Fruit$ (not $Apple$)

public class inherit2 {
public static void main(String[] args) {

Apple A = new Apple(); // create an $Apple$ object

}
}

/* Output:
1
*/

// INHERIT3: See INHERIT2 for notes.

// What's different here?
// I added $int k=2$ to class $Apple$.

class Fruit {
int k=1;
Fruit() {
print();
}
void print() {
System.out.println(k);
}
}

class Apple extends Fruit {
int k=2;
}

// When java calls $super()$ which calls $Fruit()$, which $k$ does
// $print$ call? The $k$ from $Apple$ or $Fruit$? $Fruit$, because the
// constructor in $Fruit$ "lives" in $Fruit$. So, $Fruit$'s version of
// $print$ is called, NOT $Apple$'s.

public class inherit3 {
public static void main(String[] args) {

Apple A = new Apple(); // create an $Apple$ object
// output: 1
System.out.println(A.k);
// output: 2
}
}


// INHERIT4

// a little bit on class hierarchy
// notice how I'm using variables defined in each subclass

public class inherit4 {
public static void main(String args[]) {
System.out.println(Data121.a121); // Output: 121
System.out.println(Data12.a12); // Output: 12
System.out.println(Data1.a1); // Output: 1
}
}

class Data1 { static int a1=1; }
class Data11 extends Data1 { static int a11=11; }
class Data12 extends Data1 { static int a12=12; }
class Data121 extends Data12 { static int a121=121; }


// INHERIT5: using INHERIT4
// a little bit more on class hierarchy

// You may access inherited variables (both instance and class) as long as the
// variables are VISIBLE.

// What makes a variable visible? The instance or class variable is
// modified as with no modifier, $public$, or $protected$.

public class inherit5 {
public static void main(String args[]) {

// From class $Data121$, access a variety of inherited class
// variables. Note that $Data121$ does not have $Data11$ as a
// superclass.

System.out.println(Data121.a121); // Output: 121
// System.out.println(Data121.a11); // this won't work!
System.out.println(Data121.a12); // Output: 12
System.out.println(Data121.a1); // Output: 1

}
}

class Data1 { static int a1=1; }
class Data11 extends Data1 { static int a11=11; }
class Data12 extends Data1 { static int a12=12; }
class Data121 extends Data12 { static int a121=121;}


// INHERIT6
// more exploration of what inherits for a subclass
// note: I still haven't said much about encapsulation yet!

public class inherit6 {
public static void main(String args[]) {
Data2 a = new Data2();
System.out.println(a.m); // $a$ can "see" info in superclass $Data1$
System.out.println(a.n); // $a$ can "see" info in its own class
}
}

class Data1 {
int m = 1;
}
class Data2 extends Data1 {
int n = 2;
}

/* output
1
2
*/


// INHERIT7
// more exploration of what inherits for a subclass
// now I'm exploring more with constructors
// note: I still haven't said much about encapsulation yet!

public class inherit7 {
public static void main(String args[]) {

// Call the constructor w/2 arguments from $Data2$
new Data2(0, 1); // outputs m=0, n=1

// Call the constructor w/1 argument from $Data2$
new Data2(2); // outputs m=2, n=3

// Call the constructor w/0 arguments from $Data2$
new Data2(); // outputs m=0, n=0

}
}

class Data1 {
int m;
int n;
Data1() { }
Data1(int m, int n) {
this.m = m;
this.n = n;
}
void print() {
System.out.println("m: " + m);
System.out.println("n: " + n);
}
}

// Class $Data2$ inherits instance variables $m$ and $n$ from $Data1$.
// Class $Data2$ inherits instance method $print$ from $Data1$.
// Note that subclasses NEVER inherit constructors! Instead you or Java will
// call the $super$ constructor from the superclass.

// Below, I give 3 constructors for $Data2$. In the first 2, I give provide
// the call to $super$. Java will look in class $Data1$ for the constructor
// that matches each $super$ call. Java then performs ALL code inside in the
// superclass's constructor. Often you make this kind of $super$ call to set
// instance variables.

// What about the third constructor below? I must write $Data2() {}$ if I
// wish to use the empty constructor. Why? Because I wrote another constructor,
// Java doesn't use the default constructor. Do I have to provide an empty
// constructor? In this case, yes, because I access an empty constructor
// elsewhere in the program (look inside the Main Class). Now, what does that
// mean for the superclass $Data1$? Since I used a non-empty constructor in
// $Data1$, I must now either:
// + write my own $super$ call as the first line in the empty
// constructor in $data2()$
// + write an empty constructor in $Data1$ because Java will call
// $super()$ by default.
// Below, I let Java call $super()$, so I wrote an empty constructor
// in $Data1$.

class Data2 extends Data1 {
Data2(int x, int y) {
super(x, y); // Java will call $super$ if you do not!
print(); // call inherited method $print$
}
Data2(int x) {
super(x,x+1);
print();
}
Data2() {
print();
}
}

/* output
m: 0
n: 1
m: 2
n: 3
m: 0
n: 0
*/


// INHERIT8: demonstrate how $super$ can help classes set instance variables
// Calling $super$ means calling a constructor in a superclass.
// But, any values that Java sets occur in the subclass.

public class inherit8 {
public static void main(String args[]) {

Data1 d1 = new Data1(1);
System.out.println(d1.m); // Output: 1

// Does $Data2$ "store" a value in $Data1$'s instance variable $m$?
// Create a $Data2$ object:
Data2 d2 = new Data2(2);

// Now check the $Data1$ object referred to by $d1$:
System.out.println(d1.m); // Output: 1

// Creating a $Data1$ object does not affect the $Data2$ object.
// $d1$ and $d2$ refer to entirely different objects!
// When $d2$ calls $super$, the value 2 is passed to a $Data1$
// constructor, which assigns the value 2 to instance variable
// $m$ inside $Data2$. Why? $Data2$ inherits $m$.

// So, The value of $m$ inside the object referred by $d2$
// should be 2:
System.out.println(d2.m); // Output: 2

}
}

class Data1 {
int m;
Data1(int m) {
this.m = m;
}
} // class Data1

class Data2 extends Data1 {
Data2(int m) {
super(m);
}
} // class Data2

/* output:
1
1
2
*/


public class poly {
public static void main(String[] args) {

A a1 = new A();
A a2 = new A();
B b1 = new B();
B b2 = new B();

a1 = b1;
// b2 = (B) a2; // compiles, but won't run
}

}

class A {}
class B extends A {}