【JavaSilver】継承とポリモフィズム

# 継承とポリモフィズム					
					
* サブクラスをインスタンス化すると、初期化処理は「スーパークラスのコンストラクタ」から実行し、「サブクラスのコンストラクタ」が実行される。			
					
* スーパークラス型でインスタンス化しても、オーバーライドしているメソッドが優先されるため、サブクラスでメソッドがオーバーライドされているとサブクラスのメソッドが実行される。			
					
* スーパークラス型の変数からオーバーライドしていないサブクラス側のメソッドは呼び出せない。			
					
* スーパークラスのコンストラクタを呼びだすには、super()を使う。
* 自クラス内の他のコンストラクタを呼ぶ際にthisを使用する。			

* 箱の型 変数名= new 中身の型();				
* 箱の型…どのメソッドを「呼べるか」を決定する。				
* 中身の型…メソッドが呼ばれたら、「どう動くか」を決定する。				
					
* staticメソッドからインスタンスの呼び出しは不可。				
→staticなメソッドから、（同一クラス内の）非staticなメソッドへの			
（直接の）アクセスはできないという意味。			
					
* ポリモフィズムはstaticメンバには適用されず、変数宣言時の左側の型のメソッドが呼び出される。

 ```java:例題
class Animal {			
    public static void saySomething(){		
        System.out.print("a");	
    }		
}			
```
```java
class Cow extends Animal {			
    public static void saySomething(){		
        System.out.print("b");	
    } 		
    public static void main(String[] args) {		
        Amimal[] animals = {new Animal(), new Cow()};	
        for(Animal a : animals) {	
            a.saySomething(); 
        }	
    }		
}
```
```:実行結果
aa
```		
					
* 初期化子

```java:例題
public class Sample {			
    Sample() {		
        System.out.println("A");	
    }		
    {		
    System.out.println("B");	
    }		
}
```

```:実行結果
BA
```
					
* 継承関係にある2つので同名のフィールドが使われているとき				
* フィールドを参照した場合には、左側の型のほうを使う。				
* メソッドを呼び出した場合には、メソッド内の指示に従う。

```java:例題
class A {			
    String val = "A";		
    void print() {		
        System.out.print(val);	
    }		
}			
					
class B extends A {			
    String val = "B";		
}

public class Main {			
    public static void main(Stirng[] args) {		
        A a = new A();	
        B b = new B();	
        System.out.print(a.val);	
        System.out.print(b.val);	
        a.print();	
        b.print();	
    }		
}
```
```:実行結果
ABAA
```			

```java:例題
class SuperClass {				
    public void method1() {			
        System.out.println("スーパークラスのメソッド");		
    }			
}				
```
```java
class SubClass extends SuperClass {				
    public void method2() {			
        System.out.println("サブクラスのメソッド");		
    }			
}				
```
```java
public class practice {				
    public static void main(String[] args) {			
        SubClass sb = new SubClass();		
					
        //サブクラスはSubClassとSuperClassのメソッドを両方使える		
        sb.method1();  //スーパークラスのメソッドと表示		
        sb.method2();  //サブクラスクラスのメソッドと表示		
					
        /*サブクラスのメソッドは使えない*/		
        SuperClass sp = new SuperClass();		
        sp.method1(); //スーパークラスのメソッドと表示		
    }			
}				
```

* nullに対するstaticメンバの呼び出し。				
staticメンバはクラスが読み込まれれば利用可能。			
obj変数はMain型の変数であり、Mainクラスが読み込まれたことで、このobjを使用してstaticメンバへのアクセスが可能。			
したがってNullPointerException例外は発生せず、「hello」の出力となる。

```java:例題
public class Main {			
    public static void foo(String... args) {		
        System.out.println("hello");	
        for(String s : args) {	
            System.out.println(s);
        }	
    }		
    public static void main(String[] args) {		
        Main obj = null;	
        obj.foo(); // →配列なしとして呼び出せる。	
    }		
}
```
```:実行結果
hello
```		

```java:例題
class Parent {				
    String name;			
    String getName() {			
        return this.name; 			
    }			
}				
```
```java
public class Child extends Parent {				
    String name;			
}				
```
```java
public class Main {				
    public static void main(String[] args) {			
        Child child = new Child ();		
        child.name = "sample";		
		System.out.println(child.getName());		
	}			
}
```
```:実行結果
null
```					
					
* privateメソッドをオーバーライドした際の挙動

```java:例題
public class A {			
    private void print() {		
        System.out.print("A"); 	
    }		
    public void a() {		
        print();	
    }		
}			
```
```java
public class B extends A {			
    private void print() {		
        System.out.print("B"); 	
    }		
    public void b() {		
        print();	
    }		
}			
					
public class Main {			
    public static void main(String... args) {		
        B b = new B();	
        b.a(); // →Aを出力	
        b.b(); // →Bを出力
    }		
}
```

```:実行結果
AB
※privateメソッドはオーバーライドできない			
private以外に変更すると「BB」	
```			

```java:例題
public class Animal {				
    public void eat(double quantity) {			
        System.out.println("動物は" + quantity + "g食べました。");		
    }			
}				
```			
```java			
public class Human extends Animal {
    // オーバーライドではなく、オーバーロードのため
    public void eat(int quantity) {	
        System.out.println("人間は" + quantity + "g食べました。");		
    }			
}				
```			
```java			
public class Test {				
    public static void main(String[] args) {			
        Animal animal = new Human();		
        int quantity = 500;		
        animal.eat(quantity);		
    }			
}
```
```:実行結果
動物は500.0g食べました。
```					

```java:例題
interface A { void method(); }				
class B implements A {				
    public void method() {			
        System.out.println("B"); 		
    }			
}				
```
```java
class C implements A {				
    public void method() {			
        System.out.println("C"); 		
    }			
}
```
```java 
public class Main {				
    public static void main(String[] args) {			
        A obj1 = new B();		
        A obj2 = new C();		
        A obj3 = (A)obj2;		
        obj1 = obj3;		
        obj1.method();		
    }			
}
```
```:実行結果
C
```	


```java:例題
public class Sample {
    String name;
    int num;
    public Sample(String name, int num) {
        this.name = name;
        this.num = num;	
    }		
}		
					
public class SubSample extends Sample {				
    int price;
    public SubSample(int price) {
        // ここでコンパイルエラー
        // 明示的に指定していない場合デフォルトコンストラクタが自動的に追加
        // super();
        this.price = price;
    }
    public SubSample(String name, int num, int price) {
        super(name, num);
        // ここでコンパイルエラー
        /* スーパークラスのコンストラクタを呼び出した後は、
        オーバーロードしたコンストラクタからは呼び出すことはできない。*/
        this(price);
    }
}
 
public class Main {				
    public static void main(String[] args) {			
        SubSample s1 = new SubSample(100);		
        SubSample s2 = new SubSample("sample", 200, 100);		
        System.out.println(s1.name + "," + s1.num + "," + s1.price);
        System.out.println(s2.name + "," + s2.num + "," + s2.price);
    }			
}
```
```:実行結果
subSampleクラスでコンパイルエラー
```	

* staticメソッドからインスタンス変数を呼び出しているためコンパイルエラー
```java:例題
public class Test {			
    int num = 0;		
    static void add1() {		
        num++; // コンパイルエラー	
    }		
    void add2() {		
        num++;	
    }
    public static void main(String[] args) {		
        Test t = new Test();	
        Test.add1();	
        t.add2();	
        System.out.println(t.num);	
    }		
}
```

* 引数なしのコンストラクタでフィールドの値を初期化していないためコンパイルエラー
```java:例題
public class Sample {			
    private final int num;		
    public Sample(String str) {		
        this(Integer.parseInt(str));	
    }		
    public Sample(int num) {		
        this.num = num;	
    }
    public Sample() {}
    public int getNum() {		
        return this.num;	
    }	
}
```
```:実行結果
コンパイルエラー
```

* コンストラクタが1つも定義されていないため、コンパイルエラー

```java:例題
public class Customer {			
    private String name;		
    public void Customer() {		
        name = "unknown";	
    }	
    public void Customer(String name) {		
        this.name = name;	
    }		
    public void disp() {		
        System.out.println(name);	
    }		
    public static void main(String[] args) {		
        Customer c = new Customer("Duke");	
        c.disp();	
    }		
}
```
```:実行結果
コンパイルエラー
```

```java:例題						
public class A {			
    public void hello() {		
        // any code 	
    }		
}			
						
public class B extends A {			
    public void sample () {		
        // any code	
    }		
}			
						
public class Main {			
    public static void main(String[] args) {		
        A a = new B();
        // Aクラスには存在しないメソッドを呼び出すとコンパイルエラー
        a.sample(); 	
    }		
}
```
```:実行結果
コンパイルエラー
```

```java:例題
public class Sample {				
    public void test() {			
        System.out.println("Sample#test()");		
    }			
}
```
```java					
public class SubSample extends Sample {				
    @Override
    public void test () {			
        System.out.println("SubSample#test()");		
    }			
}
```
```java					
public class Main {				
    public static void main(String[] args) {			
        SubSample sub = new SubSample();		
        Sample sample = new Sample();		
        sub = sample; // キャスト式で記述していないため		
        sub.test();		
        sample.test();		
    }			
}
```
```:実行結果
コンパイルエラー
```
* superがインタフェースBとCのどちらを表すか明確ではないためコンパイルエラーが発生

 ```java:例題
public interface A {			
    void sample();		
}		
```
```java
public interface B extends A {			
    default void sample() { 		
        System.out.println("B");	
    }		
}			
```
```java
public interface C extends A {			
    @Override		
    default void sample() { 		
        System.out.println("C");	
    }		
}
```
```java					
public class D implements B, C {			
    @Override		
    public void sample() { 		
        super.sample();
        // 下記だとコンパイルエラーが解消する
        // B.super.sample();
        // C.super.sample();
    }		
}
```
```:実行結果
コンパイルエラー
```

```java:例題
class A {			
    void hello() {		
        System.out.println("A");	
    }		
}			
```
```java
class B extends A {			
    void hello() {		
        System.out.println("B");	
    }		
}
```
```java				
public class Main {			
    public static void main(String[] args) {		
        A a = new A();	
        B b = (B)a;
        /*AのインスタンスにはBの差分が含まれていないため、				
        Bでオーバーライドしたメソッドを実行できない。*/
        b.hello();	
    }		
}								
```
```:実行結果
実行時エラー
```

```java:例題
class X { }				
class Y extends X { }				
class Z extends X { }				
public class Main {				
    public static void main(String[] args) {			
        X obj1 = new X();		
        Y obj2 = new Y();		
        X obj3 = new Z();		
        X obj4 = (X) obj2;		
        X obj5 = (Y) obj3; // 実行時エラー		
    }			
}
```
```:実行結果
実行時エラー
```