티스토리 뷰

1.    Final 클래스와 final 메소드

-      상속할 수 없는 final 클래스 : 자식 클래스를 만들지 못하도록 final 클래스로 생성한다.

-      오버 라이딩 불가능한 final 메소드 : 자식클래스가 재정의 할 수 없도록 부모 클래스의 메소드를 final로 생성한다.

-      Final 키워드의 용도 :

-      1 ) final 필드 : 수정이 불가능한 필드

-      2 ) final 클래스 : 부모로 사용이 불가능한 클래스

-      3 ) final 메소드 : 자식이 재정의할 수 없는 메소드

 

2.    Protected 접근제한자

-      Protected 접근제한자 = default 접근제한자 + alpha

-      같은 패키지 내에서는 default 접근제한자와 같이 허용되지만

-      다른 패키지에서는 default와 다르게 자식 클래스에서는! 접근이 허용된다.

-      클래스 생성시 superclass를 활용하여 부모를 선택할 수 있다.

 

3.    요약

-      1 ) 부모객체의 필드와 메소드 중에 상속이 불가능한 조건 2가지!

-        ( 1 ) private 접급제한자를 가지는 필드와 메소드는 상속이 불가능 하다.

-        ( 2 ) 다른 패키지에 자식 클래스가 있을 때, default 접근제한을 가지는 부모객체의 필드. 메소드 역시 접근이 불가능하다.

-      2 ) 다형성 ( OOP의 가장 핵심 개념 )

-      ( 1 ) 목적 : 부품관계의 필드에 실제 부품객체를 조립할 수 있는 수단이다.

-      ( 2 ) 전재 조건 : 부모 / 자식 관계의 상속관계를 요구한다.

-      ( 3 ) 2개의 전재조건이 이루어진다면, 부모타입 참조변수 = 자식객체;가 성립된다.

-      ( 4 ) 또한 상속받은 부모객체의 메소드를 재정의했다면, 부모객체.메소드( )를 통해 자식객체에서 재정의된 메소드가 호출된다!

-       + 부모객체가 자식객체보다 먼저 생성되어야 한다.

 

4.     프로모션 ( 자동형변환 )

-      프로그램 실행 도중에 자동 타입 변환이 일어나는 것을 말한다.

-      바로 위의 부모가 아니더라도 상속 계층의 상위이면, 자동타입변환 가능하다.

-      변환 후에는 부모 클래스 멤버에만 접근이 가능하다.

 


01. final 클래스

 

package Practice;

 

public final class Member { // final 클래스 - 자식 생성이 불가능 하다.

      

       ;;

 

} // end class


package Practice;

 

//public class VeryVeryImportantPerson extends Member { // Member가 final 클래스이기에 상속이 불가능하다.

 

public class VeryVeryImportantPerson {

      

       ;;

 

} // end class

 

02. final 메소드

 

package PCar;

 

public class Car { // final 메소드 - 오버라이딩 X

      

       // 1. 상태 필드

       public int speed;

      

       // 2. 인그턴스 메소드

       public void speedUp() {

             speed += 1;

       } // speedUP

      

       // 3. final 메소드

       public final void stop() {

             System.out.println("차를 멈춤");

             speed = 0;

       } // stop

 

} // end class


package PCar;

 

public class SportsCar extends Car {

      

       @Override // 자바컴파일러가 컴파일 시에, 이 메소드가 재정의 규칙에 맞게 되었는지 강하게 검사

       public void speedUp() {

             speed += 10;

       } // speedUp

      

      

//     final 메소드는 오버라이딩 할 수 없음

      

//     @Override

//     public void stop () {

//           System.out.println("스포츠카를 멈춤");

//           speed = 0;

//     } // stop

 

} // end class

 

03. protected 접근제한자

 

package org.zerock.myapp.p1;

 

public class A { // protected 접근제한자

      

       protected String field;

      

       protected A () {

             ;;

       } // default constructor

      

       protected void method () {

             System.out.println("A::method() invokes");

       } // method

 

} // end class


package org.zerock.myapp.p1;

 

public class B { // + 상속관계는 아니다!!

      

       public void method () {

             A a = new A(); // protected method를 가져오기 위한 객체 생성

            

             a.field = "뭐야"; // 불러오기 성공!

             a.method(); // 불러오기 성공!

            

       } // method

 

} // end class


package org.zerock.myapp.p2;

 

import org.zerock.myapp.p1.A;

 

public class C {

      

       public void method () {

            

//           A a = new A ( );

             // 클래스 자체는 public이어서 접근은 가능하지만, 객체를 생성하기 위한 생성자가 A클래스에서 protected로 막혀 있다.

            

//           a.field = "뭐야";

//           a.method();

             // 동일한 이유로 필드나 메소드 불러오기도 불가능 하다.

            

       } // method

 

} // end class


package org.zerock.myapp.p2;

 

import org.zerock.myapp.p1.A; // import문 까먹지 말자!!!

 

public class D extends A { // D A extends한다.

      

       public D () { // protected 불러오기 (****)

            

             // protected는 자식 생성자 내부에서 super을 통한 부모를 생성한 후에 사용할 수 있다!!(*****)

            

             super (); // 부모 생성자 생성

            

             // 부모 객체 생성은 생성자(constructor) 내에서 생성이 가능하지만!

             // 1번 부모객체를 생성했다면, 밑의 메소드에서도 사용이 가능하다.

            

             // 단, 부모가 가지고 있는 모든 필드와 메소드를 가지고 오는 것이 아니라,

             // 다른 패키지나 다른 클래스에서 접근이 불가능한 private와 default는 상속 대상에서 제외된다. (***)

            

//     ============================================================================

            

//           protected의 진짜 alpha의 의미!!! (*****)

            

             this.field = "Yoseph"; // OK!

             this.method(); // OK!

            

       } //default constructor

      

       void method2() {

            

//           A a = new A(); // protected 접근제한자의 경우, 생성자에서만!! 부모객체 생성이 가능하다!!

             // 이때는 부모자식관계로 생성한 것이 아니라, 단순히 A클래스 객체를 생성한 것이다.

            

             this.field = ""; // OK!

             this.method(); // OK!

             // 출력 : D::method3() invoked

            

//     ============================================================================

            

             super.field = "뭐인가여"; // 부모 객체의 필드를 불러오는 것!! " super. " 기억하기!! (***)

             super.method(); // 부모 객체의 메소드를 불러오는 것

             // 출력 : A::method() invokes

            

//     ============================================================================

            

             // 자식 클래스가 부모 클래스로부터 필드와 메소드를 상속받게 될 시, (****)

             // 부모 객체의 필드와 메소드가 사라지는 것은 아니다.

            

             // this. 은 상속받은 필드와 메소드를 사용하겟다는 의미이고,

             // super. 은 부모 객체에 있는 필드와 메소드를 그대로 쓰겠다는 의미이다.

            

             // 이는 오버 라이딩할 때 주의해서 사용해야 한다.

            

             // 재정의를 할때, this.을 사용하면 재정의한 것이 super.을 활용하면 부모객체의 오리지날이 나타난다.

            

       } // method2

      

       @Override // 오버 라이딩

       protected void method() {

             System.out.println("D::method3() invoked");

       } // method3

      

       public static void main (String[] args) {

            

             D d = new D ();

            

             d.method2();

            

       } // main

 

} // end class

 

04. protected 접근제한자의 응용

 

package org.zerock.myapp.p1;

 

public class Parent {

      

       protected String name = "Cho";

      

       protected Parent() {

            

             System.out.println("Parent::default constructor invoked.");

            

       } // default constructor

      

       protected void parentmethod() {

            

             System.out.println("Parent::parentmethod invoked.");

            

       } // parentmethod

 

} // end class


package org.zerock.myapp.p1;

 

public class child extends Parent {

      

       child() {

            

             super(); // OK!

            

             System.out.println("Child::default constructor invoked.");

             System.out.println("\t + name : " + this.name);

             this.parentmethod();

             super.parentmethod();

            

       } // default constructor

      

       void method () {

            

             System.out.println("Child::method invoked.");

            

             Parent parent = new Parent();

            

             System.out.println(parent);

             System.out.println("\t + name : " + super.name);

             this.parentmethod();

             super.parentmethod();

            

       } // method

      

       {

             Parent parent = new Parent();

             System.out.println(parent.name);

             parent.parentmethod();

            

       } // User - defined block

      

//     =======================================================================

      

       public static void main (String[] args) {

            

             child child1 = new child ();

             child1.method();

            

//           =====================================================================

            

//           출력 결과 :

            

//           Parent::default constructor invoked.

//           Parent::default constructor invoked.

//           Cho

//           Parent::parentmethod invoked.

//           Child::default constructor invoked.

//                  + name : Cho

//           Parent::parentmethod invoked.

//           Parent::parentmethod invoked.

//           Child::method invoked.

//           Parent::default constructor invoked.

//           org.zerock.myapp.p1.Parent@372f7a8d

//                  + name : Cho

//           Parent::parentmethod invoked.

//           Parent::parentmethod invoked.

            

       } // main

 

} // end class


package org.zerock.myapp.p1;

 

public class Other {

      

       Other() {

            

             // 사용관계

             Parent parent = new Parent();

             System.out.println(parent.name);

             parent.parentmethod();

            

             // Other은 부모자식관계가 아니어서 super을 사용할 수는 없지만

             // 같은 클래스 내에 있기 때문에 사용관계로 사용할 수 있다.

            

//           this.parentmethod();   

//           super.parentmethod();

             //  컴파일 오류 발생 : 부모자식관계이가 아니기에 this.이 불가능하다. 호출메소드는 상속된 것이 아니다.

            

       } // default constructor

      

       void OtherMethod () {

            

             // 사용관계

             Parent parent = new Parent();

             System.out.println(parent.name);

             parent.parentmethod();

            

             // 호출메소드는 상속된 것이 아니다!!

//           this.parentmethod();   

//           super.parentmethod();

            

       } // OtherMethod

 

} // end class

 

05. protected 접근제한자의 응용 - 상속관계에 따른

 

package org.zerock.myapp.p2;

 

import org.zerock.myapp.p1.Parent;

 

public class Child2 { // 상속관계가 없을 때의 protected

      

       Child2 () {

            

//           Parent parent = new Parent (); // 오류발생 : protected이기는 하지만 부모자식관계가 형성되지 않았기에 오류가 발생한다.

//           parent.name

//           parent.parentmethod();

            

       } // default constructor

      

       void Child2Method () {

            

//           Parent parent = new Parent (); // 오류발생

            

             ;;

            

       } // Child2Method

       {

      

//           Parent parent = new Parent (); // 오류발생

            

       } // User - defined block

 

} // end class


package org.zerock.myapp.p2;

 

import org.zerock.myapp.p1.Parent;

 

 

// protected = default + alpha 공식에서

// 이미 (1) default 접근제한은 통과하지 못하고

// (2) alpha 예외 조건으로 접근이 가능한가?

 

public class Child3 extends Parent { // 상속관계가 있을때의 protected

      

       Child3( ) {

            

             super(); // OK!

            

//           Parent parent = new Parent();  // 오류 :  부모 클래스의 객체가 아직 초기화되지 않았다.

             // 다른 패키지에서 Parent를 생성하기 위해서는 먼저, 부모 객체를 호출한 후에 사용이 가능하다.

            

             // protected 생성자의 의미 :

//           이 클래스에서는 자식 객체의 생성자가 호출이 될 때, 부모객체의 초기화가 먼저 수행되어야 하는데

//           이때 부모클래스의 생성자가 protected일때, 과연 부모생성자 호출이 가능한가?

//           결과 : protected = default ( X ) + alpha ( O )

//           , Parent parent = new Parent(); 이렇게 호출하는 것이 아니라, super();로 불러오라는 이야기이다.

            

       } // default constructor

      

       void Child3Method() {

            

//           Parent parent = new Parent();  // 오류 : 부모 클래스의 객체가 아직 초기화되지 않았다.

            

             super.parentmethod(); // OK!

             this.parentmethod(); // OK!

            

       } // Child3Method

 

} // end class

 

06. 다형성 ( polymorphism ) - 타이어 교체

 

package PracticePolymorphism;

 

public class Tire {

      

      

       // 1. 고유필드

      

       public int maxRotation; // 최대 회전수 ( 최대 수명 )

       public String location; // 타이어의 위치

      

       // 2. 상태필드

      

       public int accumulatedRotation; // 누적 회전수

      

       //3. 생성자

      

       public Tire (String location, int maxRotation) {

            

             this.location = location;

             this.maxRotation = maxRotation;

            

       } // Constructor

      

       // 4. 메소드

      

       public boolean roll() {

            

             ++ accumulatedRotation;

            

             if(accumulatedRotation < maxRotation) {

                   

                    System.out.println(location + " Tire 수명 : " + ( maxRotation - accumulatedRotation ) + "");

                   

                    return true;

                   

             } else {

                   

                    System.out.println("***" + location + "Tire 펑크 ***");

                   

                    return false;

                   

             }// if - else

            

       } // roll method

 

} // end class


package PracticePolymorphism;

 

public class KumhoTire extends Tire {

      

       // 1. 필드

      

       // 2. 생성자

      

       public KumhoTire ( String location, int maxRotation) {

            

             super(location, maxRotation);

            

       } // constructor

      

       // 3. 메소드

      

       @Override

       public boolean roll () {

            

             ++ accumulatedRotation;

            

             if ( accumulatedRotation < maxRotation) {

                   

                    System.out.println(location + " Kumho Tire 수명 : " + ( maxRotation - accumulatedRotation ) + "");

                   

                    return true;

                   

             } else {

                   

                    System.out.println("***" + location + "Tire 펑크 ***");

                   

                    return false;

                   

             }// if - else

            

       } // roll

 

} // end class


package PracticePolymorphism;

 

public class HankookTire extends Tire {

      

       // 1. 필드

      

       // 2. 생성자

      

       public HankookTire ( String location, int maxRotation) {

            

             super(location, maxRotation);

            

       } // constructor

      

       // 3. 메소드

      

       @Override

       public boolean roll () {

            

             ++ accumulatedRotation;

            

             if ( accumulatedRotation < maxRotation) {

                   

                    System.out.println(location + " Hankook Tire 수명 : "

                    + ( maxRotation - accumulatedRotation ) + "");

                   

                    return true;

                   

             } else {

                   

                    System.out.println("***" + location + "Tire 펑크 ***");

                   

                    return false;

                   

             }// if - else

            

       } // roll

 

 

} // end class


import lombok.NoArgsConstructor;

 

@NoArgsConstructor

public class Car {

      

       // 1. 부품 필드 ( 집합 관계 )

       Tire frontLeftTire = new Tire ( "앞 왼쪽", 6);

       Tire frontRightTire = new Tire ( "앞 오른쪽", 2);

       Tire backLeftTire = new Tire ( "뒤 왼쪽", 3);

       Tire backRightTire = new Tire ( "뒤 오른쪽", 4);

      

       // 2. 생성자

      

      

       // 3. 메소드

      

       int run ( ) { // 다형성 2 - 오버 라이드

            

             System.out.println(" [ 자동차가 달립니다! ] ");

            

             if ( frontLeftTire.roll() == false ) { stop(); return 1; };

             if ( frontRightTire.roll() == false ) { stop(); return 2; };

             if ( backLeftTire.roll() == false ) { stop(); return 3; };

             if ( backRightTire.roll() == false ) { stop(); return 4; };

            

             return 0;

            

       } // run

      

       void stop () {

            

             System.out.println(" [ 자동차가 멈춥니다... ] ");

            

       } // stop

 

} // end class


package PracticePolymorphism;

 

public class CarExample {

      

       public static void main (String [] args) {

            

             Car car = new Car ( );

            

             for ( int i = 1; i <= 5; i++ ) {

                   

                    int problemLocation = car.run();

                   

                    switch(problemLocation) {

                   

                           case 1 : // 다형성 1 - car.frontLeftTire Tire타입이다.

                                 System.out.println("앞 왼쪽 HankookTire로 교체");

                                 car.frontLeftTire = new HankookTire("앞 왼쪽",15);

                                 break;

                          

                           case 2 : // 다형성 1

                                 System.out.println("앞 왼쪽 HankookTire로 교체");

                                 car.frontRightTire = new HankookTire("앞 오른쪽",13);

                                 break;

                          

                           case 3 : // 다형성 1

                                 System.out.println("뒤 왼쪽 HankookTire로 교체");

                                 car.backLeftTire = new HankookTire("뒤 왼쪽",14);

                                 break;

                          

                           case 4 : // 다형성 1

                                 System.out.println("뒤 오른쪽 HankookTire로 교체");

                                 car.backRightTire = new HankookTire("뒤 오른쪽",17);

                                 break;

                   

                           } // switch

                   

                    System.out.println("==========================================================");

                   

             } // for

            

       } // main

 

} // end class

 

07. 프로모션 01 - 프로모션( 강제형변환 )의 필요성

 

package PracticePolymorphism02;

 

 

// 다형성 1에 의해서 부모타입의 참조변수에 들어가 있는,

// 자식객체의 고유한 필드와 메소드를 어떻게 사용하는가?

public class Parent {

      

       public String field1;

      

       public void method1() {

             System.out.println("Parent:method1() invoked.");

       } // method1

      

       public void method2() {

             System.out.println("Parent:method2() invoked.");

       } // method

 

} // end class


package PracticePolymorphism02;

 

import lombok.AccessLevel;

import lombok.NoArgsConstructor;

 

@NoArgsConstructor(access=AccessLevel.PUBLIC)

public class Child extends Parent {

      

       public String field2;

      

       public void method3() {

             System.out.println("Child::method3() invoked.");

       } // method3

 

} // end class


package PracticePolymorphism02;

 

public class ChildExample { // 강제형변환의 필요성 (***)

      

       public static void main ( String[] args) {

            

             Parent parent = new Child (); // 다형성 1

            

             // 다형성 1에 의해 부모타입에 선언된 필드와 메소드만 사용가능

             // 물론, 부모 메소드 중에 자식객체에서 재정의된 메소드가 있다면

             // 다형성 2에 의해 자식객체의 재정의된 메소드가 호출됨

            

             parent.field1 = "data 1";

             System.out.println(parent.field1);

             parent.method1();

             parent.method2();

//           출력 :

//           data 1

//           Parent:method1() invoked.

//           Parent:method2() invoked.

            

//           =======================================================================

            

//           child.field2 = "data 2"; // 불가능!

//           child.method3();           // 불가능!

            

//           =======================================================================

            

             // 다형성1에 의해 부모 타입의 참조변수에 저장된 자식객체를 다시 끄집어 낸다!

             // 강제형변환 필요!!! (****)

             // 이때에는 주소를 가지고 있기 때문에 강제형변환의 부작용인 데이터 유실이 없다!!

            

             Child child = ( Child ) parent;

             child.field2 = " yyy "; // 가능!

             System.out.println(child.field2);

             child.method3(); // 가능!

//           출력 :

//           yyy

//           Child::method3() invoked.

            

       } // main

 

} // end class

 

08. 프로모션 02

 

package PracticePromotion;

 

public class PromotionExample { // 프로모션 (****)

      

       // 다형성 1 - 실험 :

//     부모타입 참조변수 = 자식객체 ; 의 대입이 가능하다!!

//     이것이 가능한 이유는 상속관계를 통해 대/소비교가 가능하고

//     이를 통해서 자동형변환이 발생하게 된다!

      

       public static void main (String [] args ) { 

            

             B b = new B ();

             C c = new C ();

             D d = new D ();

             E e = new E ();

            

//           =========================

            

             // Lvalue( 할아버지 참조변수 ) = Rvalue( 자식 또는 후손 객체 )

            

             A a1 = b;

            

             System.out.println("1. a1 : " + a1);

             // 출력 : 1. a1 : PracticePromotion.B@1f32e575

             // b A타입의 a1에 들어갔기는 했지만, 실제로는 B타입이 출력되는 것을 볼 수 있다.

             // 이는 프로모션(자동형변환)된다하더라도 b A타입으로 변하는 것이 아니라 B타입 그대로 있는 것을 알 수 있다.

            

             A a2 = c;

             A a3 = d;

             A a4 = e;

            

             // 자식객체가 맞다면 후손도 들어갈 수 있다.

            

//           ====================

            

             B b1 = d;

            

             System.out.println("2. b1 : " + b1);

             // 출력 : 2. b1 : PracticePromotion.D@279f2327

            

             C c1 = e;

            

//           =====================

            

             // 오류가 발생하게 된다!

//           B b3 = e;

//           C c3 = d;

            

             // 모두 같은 조상객체인 A를 가지고는 있으나 서로가 서로의 부모자식관계가 아니기에 오류가 발생한다.

            

       } // main

 

} // end class

 

09. Instance of

 

package Instanceof;

 

import lombok.AccessLevel;

import lombok.NoArgsConstructor;

 

@NoArgsConstructor(access=AccessLevel.PUBLIC)

public class parent {

      

       ;;

 

} // end class


package Instanceof;

 

import lombok.NoArgsConstructor;

 

@NoArgsConstructor

public class Child extends parent {

      

       ;;

 

} // end class


package Instanceof;

 

public class InstanceofExample { // instanceof (***)

      

       // 다형성 1에 의해 부모타입의 참조변수에 저장되어 있는 자식객체를 다시 빼낼 때 ( 강제형변환 )

       // 자식타입을 잘못알고 하면, classCastException 예외가 발생할 가능성이 있기 때문이다.

       // 먼저, 자식타입을 검사해서 맞으면, 그때 안전하게 강제형변환을 통해 빼내고자

       // ** instanceof ** 라는 연산자가 필요하다!!

      

       //! instanceOf를 사용하면 이 객체가 child인지, parent로부터 상속받은 클래스의 객체인지 확인할 수 있다.

       // instanceOf는 주로 상속 관계에서 부모객체인지 자식객체인지 확인하는데 사용된다.

      

      

       public static void method1 ( parent Parent) {  // 매개변수의 다형성 1

            

             System.out.println("InstanceofExample::method1(parent) invoked");

            

             if ( Parent instanceof Child ) { // 매개변수를 활용하여 매개변수에 따라서 강제형변환을 진행할지 말지를 결정하도록 하였다.

                    Child child = (Child) Parent;                                      // 안전한 강제형변환 (***)

                    System.out.println( "method1 - child로 변환성공 ");

             } else {

                    System.out.println( "method1 - child로 변환되지 않음 ");

             } // if - else

            

       } // method1

      

       public static void method2 ( parent Parent) { // 매개변수의 다형성 1

            

             System.out.println("InstanceofExample::method2(parent) invoked");

            

             Child child = (Child) Parent;                                             // 안전하지 않은 강제형변환 (***) - 오류가 발생할 수 있다.

            

             System.out.println( "method2 - child로 변환성공 ");

       } // method1

      

       public static void main ( String [] args ) {

            

             parent parentA = new Child(); // 다형성 1 발생

             InstanceofExample.method1(parentA); // 매개변수의 다형성 OK

             InstanceofExample.method2(parentA); // 매개변수의 다형성 OK

            

//           ==============================================================

            

             parent parentB = new Child();

             InstanceofExample.method1(parentB); // 매개변수의 다형성 NO!

             InstanceofExample.method2(parentB); // 매개변수의 다형성 NO!

            

//           ==============================================================

            

             parent parentC = new parent();

             InstanceofExample.method1(parentC); // 매개변수의 다형성 NO!

             InstanceofExample.method2(parentC); // 매개변수의 다형성 NO!

            

             // parentC의 경우 실행하면 InstanceofExample.method2에서 오류가 발생하게 되는데

             // 그 이유는 InstanceofExample.method2의 경우 무조건 child가 들어온다고 생각하고 있는데 parent가 들어왔기에 오류가 발생했다.

            

       } // main

 

} // end class

728x90
댓글
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
최근에 올라온 글
Total
Today
Yesterday
공지사항