1021

란이:)·2022년 10월 21일
0

공부일지

목록 보기
11/30

다형성

하나의 인스턴스가 여러가지 타입을 가질 수 있는 것을 의미
다형성은 객체지향 프로그래밍의 3대특징(캡슐화, 상속, 다형성)중 하나이며, 객체지향 프로그램의 꽃
다형성은 상속을 기반으로 한 기술

4대특징 캡(슐호)상(속)추(상화)다(형성)

단일상속은 object까지가 단일상속
다중상속은 다른 부모까지사용하는것
fireCar가 Animal이랑 Car까지
다중상속은 여러개 사용해서 좋기는 하지만
복잡해지면 부모가 어디꺼인지 알수가 없기때문에
자바에서는 단일상속만 지원

다형성의 특징
여러타입의 객체를 하나의 타입으로 관리할 수 있으므로 유지보수성과 생산성이 증가 됨
상속관계에있는 모든 객체는 동일한 메시지를 수신할 수 있음(역할)
확장성이 좋은 코드 작성 가능
결합도를 낮춰 유지보수성을 증가시킬수있음

클래스들을 한 묶음으로 쓰기 위해 객체배열사용

객체배열은 같은 자료형이여야만 사용가능

다형성 상속을 기반으로 나를 기준으로 여러타입들을 묶어서 사용하는것이 다형성

Parent p = new Parent();
-> 객체생성
Stack = Heap공간

parent 인스턴스 변수 xy

p.x
p.y

필드변수 X 필드변수 Y

MemberDTO member = new MemberDTO()
-> 객체생성
member.name
-> MemberDTO의 필드변수 name을 사용할수 있다. member 라는 인스턴스변수(객체변수)를 생성했기 때문


Child는 Child타입이면서 Parent타입이다.
Parent는 Parent만 볼 수 있다.

아이는 부모가될수 있는데 부모는 아이가 될수 없음

p는 p.x p.y만 가능
p.z는 안됨

자식클래스에서 오버라이딩되면 부모타입의 레퍼런스 주소로참조하게되면 부모타입의 cry로 가다가 엇 이거 자식 클래스에서 재정의 했네 하고 오버라이딩되어있는 자식클래스걸로 호출한다
== 동적바인딩

컴파일 당시에는 해당타입의 메소드와 연결되어 있다가 런타임당시 실제 객체가 가진 오버라이딩 된 메소드로 바인딩이 바뀌어 동작하는 것을 의미
-동적 바인딩 조건
상속관계로 이루어져 다형성이 적용된 경우, 메소드 오버라이딩이 되어 있어야함

Animal a1 = new Rabbit();
->Animal a1 = new Animal():

자식이 재정의 했기때문에 (오버라이딩) 자식걸로 실행
자식이 권한을 물려받았다고 생각
package com.greedy.section01.polymorphism;

public class Application {

public static void main(String[] args) {
	
	/* 
	 * 다형성
	 * 
	 * 다형성이란 하나의 인스턴스가 여러 가지 타입을 가질 수 있는 것을 의미한다.
	 * 그렇기 때문에 하나의 타입으로 여러 타입의 인스턴스를 처리할 수 있기도 하고,
	 * 하나의 메소드 호출로 객체별로 각기 다른 방법으로 동작하게 할 수 있다.
	 * 
	 * 
	 * 다형성은 객체지향 프로그래밍의 3대 특징(캡슐화, 상속, 다형성) 중 하나이며
	 * 
	 * 객체지향 프로그래밍의 꽃이라고 불리울 정도로 활용성이 높고 장점이 많다.
	 * 하지만 학습하기가 어렵다는 단점이 있다.
	 * 
	 * 다형성이란 서로 다른 형태를 가지고 있는 성질을 의미한다는 것이다.
	 * 다형성은 상속을 기반으로 한 기술이다.
	 * 상속은 부모클래스가 가지는 속성과 기능(필드와 메소드)을 물려받아 자신의 것처럼 사용하며 확장도 가능한 개념이라고 했다.
	 * 하지만 이때 물려받는 것이 한 가지 더 있다 바로 !!!타입!!!이다.
	 * */
	
	System.out.println("Animal 생성 ==================================");
	
	Animal animal = new Animal();
	animal.eat();
	animal.run();
	animal.cry();
	
	System.out.println("Rabbit 생성 ==================================");
	
	Rabbit rabbit = new Rabbit();
	rabbit.eat();
	rabbit.run();
	rabbit.cry();
	rabbit.jump();
	
	System.out.println("Tiger 생성 ==================================");
	
	Tiger tiger = new Tiger();
	tiger.eat();
	tiger.run();
	tiger.cry();
	tiger.bite();
	
	/*
	 * Rabbit과 Tiger는 Animal클래스를 상속받았다.
	 * 따라서 Rabbit은 Rabbit타입이기도 하면서 Animal타입이기도 하며,
	 * Tiger는 Tiger타입이면서 Animal 타입이기도 하다.
	 * */
	
	/* 그래서 이렇게 할 수 있다.*/
	Animal a1 = new Rabbit();
	Animal a2 = new Tiger();
	
	/* 반대로 Animal은 Animal이지 Tiger나 Rabbit이 아닙니다.*/

// Rabbit r = new Animal(); // 에러난다
// Tiger t = new Animal(); // 에러난다

	System.out.println("동적바인딩 확인 =========================");
	a1.cry();
	a2.cry();
	

// a1.jump(); // 에러남
// a2.bite(); // 에러남

	System.out.println("클래스타입 형변환 확인 ===================");
	/* 객체별로 고유한 기능을 동작시키기 위해서는 레퍼런스 변수를 형변환하여 Rabbit과 Tiger로 변경해야
	 * 메소드 호출이 가능하다.
	 * 
	 * class type casting : 클래스 형변환
	 * -> 타입 형변환 시 실제 인스턴스와 타입이 일치하지 않은 경우
	 *    ClassCastException 발생한다.
	 * */
	
	((Rabbit) a1).jump(); // a1은 rabbit타입/  같이 괄호로 묶어서 먼저 형변환을 시킨다
	((Tiger) a2).bite();  
	
	/* 타입 형변환을 잘못하는 경우 */

// ((Tiger) a1).bite(); // 토끼는 호랑이가 될 수 없다.

	/* 레퍼런스 변수가 참조하는 실제인스턴스가 원하는 타입과 맞는지 비교하는 연산 instanceof */
	
	System.out.println("instanceof 확인 ===================================");
	System.out.println("a1이 Tiger 타입인지 확인 : " +(a1 instanceof Tiger));
	System.out.println("a1이 Rabbit 타입인지 확인 : " +(a1 instanceof Rabbit));
	System.out.println("a1이 Animal 타입인지 확인 : " +(a1 instanceof Animal));
	System.out.println("a1이 Object 타입인지 확인 : " +(a1 instanceof Object));
	
	if(a1 instanceof Rabbit) {
		
		((Rabbit) a1).jump();
		
	}
	
	if(a1 instanceof Tiger) {
		
		((Tiger) a2).bite();
	}
	
	/* 클래스 형변환은 up-casting과 down-casting으로 구분할 수 있다.
	 * up-casting : 상위 타입으로 형변환 / 자식이 부모타입으로 형변환
	 * down-casting : 하위 타입으로 형변환 / 부모가 자식타입으로 형변환
	 * */
	Animal animal1 = (Animal) new Rabbit(); // up-casting 명시적 형변환 
	Animal animal2 = new Rabbit();          // up-casting 묵시적 형변환 = 앞에 생략했다는 것
	
	Rabbit rabbit1 = (Rabbit) animal1; //animal1은 animal 타입이다. down-casting 명시적 형변환

// Rabbit rabbit2 = animal1; // down-casting 묵시적 형변환 안됨

}

}

a1이 Animal 타입인지 확인 : true
a1이 Object 타입인지 확인 : true
토끼가 점프합니다. 점프!!!!

package com.greedy.section01.polymorphism;

public class Animal {

/* 동물은 기본적으로 먹는 행동과 뛰는 행동 우는 행동을 할 수 있다.*/

public void eat() {
	
	System.out.println("동물이 먹이를 먹습니다.");
}

public void run() {
	
	System.out.println("동물이 달려갑니다.");
	
}

public void cry() {
	
	System.out.println("동물이 울음소리를 냅니다.");
}

}

package com.greedy.section01.polymorphism;

public class Rabbit extends Animal {

@Override
public void eat() {
	
	System.out.println("토끼가 풀을 뜯어 먹고 있습니다.");


}

@Override
public void run() {

	System.out.println("토끼가 달려갑니다.깡총~ 깡총~");
}

@Override
public void cry() {
		System.out.println("토끼가 울음소리를 냅니다. 끼익~~끼익~~~");
}

/* 추가적으로 토끼는 점프를 뛸 수 있습니다. */
public void jump() {
	
	System.out.println("토끼가 점프합니다. 점프!!!!");
}

}

package com.greedy.section01.polymorphism;

public class Application4 {

public static void main(String[] args) {
	
	Application4 app4 = new Application4();
	
	Animal randomAnimal = app4.getRandomAnimal();
	
	randomAnimal.cry(); // 동적바인딩때문에 토끼일때는 토끼가 우는걸로, 호랑이일때는 호랑이가 우는걸로 출력된다.
}

public Animal getRandomAnimal() {
	
	int random = (int) (Math.random() * 2);
	/* 0이면 Rabbit의 인스턴스를 Animal 타입에 담아서 전달하고
	 * 1이면 Tiger의 인스턴스를 Animal타입에 담아서 리턴한다.
	 * */
	return random == 0? new Rabbit() : new Tiger();
}

public String test() {
	
	return "test";     // 문자열 반환 리턴할때 string = return 타입을 맞춰줘야함
}

}
package com.greedy.section01.polymorphism;

public class Application2 {

public static void main(String[] args) {
	
	Animal[] animals = new Animal[5]; // up캐스팅 된 상태 

	animals[0] = new Rabbit();
	animals[1] = new Tiger();
    animals[2] = new Rabbit();
	animals[3] = new Tiger();
	animals[4] = new Rabbit();
	
	/* Animal 클래스가 가지는 메소드를 오버라이딩한 메소드를 호출 시 동적바인딩을 이용할 수 있다.
	 * 
	 * 토끼 울어라! 호랑이 울어라 이렇게 개별적으로 하는게 아니라
	 * 동물들 다 울어라! 이런 느낌이다.
	 * */
	for(int i = 0; i < animals.length; i++) {
		
		animals[i].cry();
	}
	
	/* 각 클래스별 고유한 메소드를 동작시키기 위해서는 down-casting을 명시적으로 해줘야하는데
	 * ClassCastException을 방지하기 위해서 instanceof 연산자를 이용한다.
	 * */
    for(int i = 0; i < animals.length; i++) {
    	
    	if(animals[i] instanceof Rabbit) {
    		
    		((Rabbit) animals[i]).jump();
    		
    	}else if (animals[i] instanceof Tiger) {
    		
    		((Tiger) animals[i]).bite();
    	}else {
    		
    		System.out.println("호랑이나 토끼가 아닙니다.");
    	}
		
		
	}

}

}

토끼가 울음소리를 냅니다. 끼익끼익~
호랑이가 울부짖습니다. 어흐으으으응!!
토끼가 울음소리를 냅니다. 끼익끼익~
호랑이가 울부짖습니다. 어흐으으으응!!
토끼가 울음소리를 냅니다. 끼익끼익~
토끼가 점프합니다. 점프!!!!
호랑이가 물어뜯습니다. 앙~
토끼가 점프합니다. 점프!!!!
호랑이가 물어뜯습니다. 앙~
토끼가 점프합니다. 점프!!!!
package com.greedy.section01.polymorphism;

public class Application4 {

public static void main(String[] args) {
	
	Application4 app4 = new Application4();
	
	Animal randomAnimal = app4.getRandomAnimal();
	
	randomAnimal.cry(); // 동적바인딩때문에 토끼일때는 토끼가 우는걸로, 호랑이일때는 호랑이가 우는걸로 출력된다.
}

public Animal getRandomAnimal() {
	
	int random = (int) (Math.random() * 2);
	/* 0이면 Rabbit의 인스턴스를 Animal 타입에 담아서 전달하고
	 * 1이면 Tiger의 인스턴스를 Animal타입에 담아서 리턴한다.
	 * */
	return random == 0? new Rabbit() : new Tiger();
}

public String test() {
	
	return "test";     // 문자열 반환 리턴할때 string = return 타입을 맞춰줘야함
}

}
package com.greedy.section01.polymorphism;

public class Application3 {

public static void main(String[] args) {
	
	/* 매개변수에도 다형성을 활용할 수 있다. */
	Application3 app3 = new Application3(); //아래 메소드는 static 이 아니기 때문에
	
	Animal animal1 = new Rabbit();
	Animal animal2 = new Tiger();
	
	app3.feed(animal1); // 동일한 타입이기 때문에
	app3.feed(animal2);
	
	Rabbit animal3 = new Rabbit();
	Tiger animal4 = new Tiger();
	
	app3.feed(animal3); // 묵시적 형변환 (up-casting)
	app3.feed(animal4);
	
	app3.feed((Animal) animal3); //명시적 형변환 (down-casting)
	app3.feed((Animal) animal4);
	
	/* 인스턴스를 생성하여 바로 묵시적 형변환을 이용해 전달할 수 도 있다.*/
	app3.feed(new Rabbit());
	app3.feed(new Tiger());
}

/**
 * <pre>
 *     동물에게 먹이를 주기 위한 용도의 메소드
 * </pre>
 * @param animal animal 먹이를 줄 동물
 */
public void feed(Animal animal) {
	
	animal.eat();
}

}
package com.greedy.section01.polymorphism;

public class Application4 {

public static void main(String[] args) {
	
	Application4 app4 = new Application4();
	
	Animal randomAnimal = app4.getRandomAnimal();
	
	randomAnimal.cry(); // 동적바인딩때문에 토끼일때는 토끼가 우는걸로, 호랑이일때는 호랑이가 우는걸로 출력된다.
}

public Animal getRandomAnimal() {
	
	int random = (int) (Math.random() * 2);
	/* 0이면 Rabbit의 인스턴스를 Animal 타입에 담아서 전달하고
	 * 1이면 Tiger의 인스턴스를 Animal타입에 담아서 리턴한다.
	 * */
	return random == 0? new Rabbit() : new Tiger();
}

public String test() {
	
	return "test";     // 문자열 반환 리턴할때 string = return 타입을 맞춰줘야함
}

}

추상클래스
-추상메소드를 0개 이상 포함하는 클래스
-추상클래스로는 인스턴스를 생성할 수 없음
-추상클래스를 사용하려면 추상클래스를 상속받은 하위 클래스를 이용해서 인스턴스를 생성(다형성을 활용)
-사용목적
추상클래스의 추상메소드는 오버라이딩에 대한 강제성이 부여됨 (필수 기능을 정의하여 강제성을 부여해 개발 시 일관된 인터페이스를 제공)

내가 기능을 구현하는데 사용자마다 사용해 처리할 결과들을 달리 처리해야할 때
모든 사용자에 대한 내용들을 다 받아서 처리하지 못할 때

컨트롤 쉬프트 S 전체 열려있는 페이지 저장

package com.greedy.section02.abstractclass;

public class Application {

public static void main(String[] args) {
	 
	/*
	 * 추상클래스와 추상메소드
	 * 
	 * 추상메소드를 0개 이상 포함하는 클래스를 추상클래스라고 한다.
	 * 추상클래스는 클래스 선언부에 abstract 키워드를 명시해야 한다.
	 * 
	 * 추상클래스로는 인스턴스를 생성할 수 없다.
	 * 추상클래스를 사용하려면 추상클래스를 상속받은 하위 클래스를 이용해서 인스턴스를 생성해야한다.
	 * 이 때 추상클래스는 상위타입으로 사용될 수 있으며, 다형성을 이용할 수 있다.
	 * 
	 * 추상클래스에 작성한 추상메소드는 반드시 후손이 오버라이딩 해서 작성해야 하며,
	 * 후손 클래스들의 메소드의 공통 인터페이스로의 역할을 수행할 수 있다.
	 * 추상클래스에 적당한 메소드를 호출하게되면 실제 후손 타입의 인스턴스가 가지는 메소드는
	 * 다형성이 적용되어 동적바인딩에 의해 다양한 응답을 할 수 있게 된다.
	 * 
	 * 추상클래스를 상속받아 구현할 때는 extends 키워드를 사용하며
	 * 자바에서는 extends로 클래스를 상속받을 시 하나의 부모 클래스만 가질 수 있다(단일상속)
	 *
	 * 추상메소드란?
	 * 메소드의 선언부만 있고 구현부가 없는 메소드를 추상메소드라고 한다.
	 * 
	 * 추상메소드의 경우 반드시 abstract 키워드를 메소드 헤드에 작성해야 한다.
	 * 예) public abstract void method();
	 * */
	
	

// Product product = new Product(); // 추상클래스는 인스턴스 생성 불가

	/* 추상클래스를 상속받은 객체를 이용해서 인스턴스를 생성한다. */
	SmartPhone smartPhone = new SmartPhone();
	
	/* SmartPhone은 SmartPhone 타입이기도 하지만 Product타입이기도 하다. */
	System.out.println(smartPhone instanceof SmartPhone);
	System.out.println(smartPhone instanceof Product);
	
	/* 따라서 다형성을 적용해서 추상클래스를 레퍼런스 타입으로 활용할 수 있다. */
	Product product = new SmartPhone(); // up casting
	
	/* 동적 바인딩에 의해 SmartPhone의 메소드가 호출된다.*/
	product.abstMethod();
	
	product.nonStaticMethod();
	
	/* static 메소드는 그냥 사용이 가능하다 (인스턴스 생성 불필요)*/
	Product.staticMethod();
}

}

package com.greedy.section02.abstractclass;

public abstract class Product {

/* 추상클래스는 필드를 가질 수  있다. */
private int nonStaticField;
private static int staticField;

/* 추상클래스는 생성자도 가질 수 있다.
 * 하지만 직접적으로 인스턴스를 생성할 수는 없다.
 * */
public Product() {} 

/* 추상클래스는 일반적인 메소드를 가질 수 있다.*/
public void nonStaticMethod() {
	
	System.out.println("Product 클래스의 nonStaitcMethod 호출함...");
}

public static void staticMethod() {
	
	System.out.println("Product 클래스의 staticMethod 호출함...");
}

/* 추가적으로 미완성메소드(추상메소드) 또한 만들 수 있다. */
public abstract void abstMethod(); //대신 해당 클래스를 무조건 추상클래스로 변경해줘야한다.

}

package com.greedy.section02.abstractclass;

public class SmartPhone extends Product /, java.util.Scanner /{

/* SmartPhone 클래스는 Product 클래스를 상속받아 구현한다.
 * 
 * extends키워드로 클래스를 상속받을 때 두 개 이상의 클래스는 상속하지 못한다.(모호성 제거)
 * 추상 클래스가 가지는 추상메소드를 반드시 오버라이딩 해아한다.(강제성 부여)
 * */
@Override
public void abstMethod() {
	
	System.out.println("Product 클래스의 abstMethod를 오버라이딩 한 메소드 호출함 ..");
}

public void printSmartPhone() {
	
	System.out.println("SmartPhone 클래스의 printSmartPhone 메소드 호출함...");
}

}

true
true
Product 클래스의 abstMethod를 오버라이딩 한 메소드 호출함 ..
Product 클래스의 nonStaitcMethod 호출함...
Product 클래스의 staticMethod 호출함...

추상클래스와 인터페이스 비교 암기(면접)

인터페이스는 , 를 사용해서 여러개를 상속받아 사용가능

재정의한 접근제한자의 값은 부모와 똑같거나 더 커야해서
자식클래스는 무조건 public이어야 한다.
package com.greedy.section03.interfaceImplements;

public class Product extends java.lang.Object implements InterProduct {

@Override
public void nonStaticMethod() {

    System.out.println("InterProduct의 nonStaticMethod 오버라이딩 메소드 호출됨...");
}

@Override
public void abstMethod() {

	System.out.println("InterProduct의 abstMethod오버라이딩한 메소드 호출됨...");
	
}
/* static 메소드는 오버라이딩 할 수 없다.*/

// @Override
// public static void staticMethod() {}

/* default 메소드는 인터페이스에서만 작성 가능하다. */

// @Override
// public void defaultMethod() {

/* default 키워드를 제외하면 오버라이딩 가능하다.*/
@Override
public void defaultMethod() {

	System.out.println("Product 클래스의 defaultMethod 호출됨..");
}

}

package com.greedy.section03.interfaceImplements;

public interface InterProduct extends java.io.Serializable/, java.util.Comparator/{

/* 인터페이스가 인터페이스를 상속받을 시에는 extends 키워드를 이용하며
 * 이 때는 여러 인터페이스를 다중 상속 받을 수 있다.
 * */

/* 인터페이스는 상수 필드만 작성 가능하다. 
 * public static final 제어자 조합을 상수 필드라고 부른다.
 * 반드시 선언과 동시에 초기화 해줘야 한다.
 * */
public static final int MAX_NUM = 100;

/* 상수 필드만을 가질 수 있기 때문에 모든 필드는 묵시적(암묵적)으로 public static final이다. */
int MIN_NUM = 10; //static이 붙어있을 때만 누워있다.

/* 인터페이스는 생성자를 가질 수 없다. */

// public InterProduct() {}

/* 추상 메소드만 작성이 가능하다. */
public abstract void nonStaticMethod();

/* 인터페이스 안에 작성한 메소드는 묵시적으로 public abstract의 의미를 가진다( 다른 접근제한자 사용 불가)
 * 
 * 따라서 인터페이스의 메소드를 오버라이딩 하는 경우
 * 반드시 접근제한자를 public으로 해야 오버라이딩이 가능하다.
 * */
void abstMethod();

/* (JDK 1.8에 추가된 기능) : static 메소드는 작성이  가능하다. */
public static void staticMethod() { 
	
	System.out.println("InterProduct 클래스의 staticMethod 호출됨..");
}

/* (JDK 1.8에 추가된 기능) : default 키워드를 사용한 non-static 메소드도 작성이 가능하다.*/
public default void defaultMethod() {
	
	System.out.println("InterProduct 클래스의 defaultMethod 호출됨.. ");
	
}

}

package com.greedy.section03.interfaceImplements;

public class Application {

public static void main(String[] args) {
	
	/* 인터페이스(interface)
	 * 여기서 다루게 되는 인터페이스는 자바의 클래스와 유사한 형태이지만
	 * 추상메소드와 상수 필드만 가질 수 있는 클래스의 변형체를 말한다.
	 * */
	
	/* 인터페이스의 사용 목적
	 * 1. 추상클래스와 비슷하게 필요한 기능을 공통화해서 강제성을 부여할 목적으로 사용한다.(표준화)
	 * 2. 자바의 단일상속의 단점을 극복할  수 있다.(다중 상속)
	 * */
	/* 인스턴스를 생성하지 못하고, 생성자 자체가 존재하지 않는다.*/

// InterProduct interProduct = new InterProduct();

	/* 레퍼런스 타입으로만 사용이 가능하다. */
	InterProduct interProduct = new Product();
	
	/* 인터페이스의 추상메소드 오버라이딩한 메소드로 동적바인딩에 의해 호출됨*/
	interProduct.nonStaticMethod();
	interProduct.abstMethod();
	
	/* 오버라이딩 하지 않으면 인터페이스의 default 메소드로 호출됨 */
	interProduct.defaultMethod();
	
	/* static 메소드는 인터페이스명.메소드명(); 으로 호출한다. */
	InterProduct.staticMethod();
	
	/* 상수 필드 접근도 인터페이스명.필드명으로 접근한다.*/
	System.out.println(InterProduct.MAX_NUM); // public final static ->
	System.out.println(InterProduct.MIN_NUM);
	
}

}

InterProduct의 nonStaticMethod 오버라이딩 메소드 호출됨...
InterProduct의 abstMethod오버라이딩한 메소드 호출됨...
Product 클래스의 defaultMethod 호출됨..
InterProduct 클래스의 staticMethod 호출됨..
100
10

profile
FE Developer 🐥

0개의 댓글