본문 바로가기
코딩수업/AWS 클라우드환경 네이티브

8/12 자바(Java) super, static, get/set 메소드, 다형성, abstract, 참조 변수의 타입 변환(up-casting / down-casting)

by 인생즐겜러 2022. 8. 12.
728x90
반응형

AWS 클라우드환경 네이티브 수업 61일차

 

 

 

진행

1. 자바에서 상속을 여러개 받는 방법 ( 8/8 상속 참고 )

2. 오버라이딩 ( 8/9 오버라이딩 참고 )

3. super

4. 접근 제어자 ( 8/5 접근제어자 참고)

5. static

6. final ( 8/5 final 참고 )

7. get / set

8. abstract

9. 다형성

10. 참조 변수의 타입 변환 (up-casting / down-casting)

 

 

 

 

 

요약

1. super

2. static

3. get / set 메소드

4. 다형성

5. abstract

6. 참조 변수의 타입 변환 (up-casting / down-casting)

 

 

 

 

 


 

 

 

 

 

super

 

상속받은 필드나 메소드를 자식 클래스에서 참조하는 데 사용하는 참조 변수.

쉽게 말해 부모한테 받은 걸 사용하기 위해 쓰는 변수.

 

this => 인스턴스 변수의 이름과 지역 변수의 이름이 같을 경우 인스턴스 변수 앞에 붙임.

super => 부모 클래스의 멤버와 자식 클래스의 멤버 이름이 같을 경우, 부모 멤버를 사용 하기 위해 자식 멤버 앞에 붙임.

 

아래의 예를 보며 이해를 해보자.

 

package object;

class parent{
	int x = 10 ;
}

class child extends parent{
	public void method() {
		System.out.println("x	=> " + x); 
		System.out.println("this.x	=> " + this.x); 
		System.out.println("super.x	=> " + super.x); 
	}
}

public class Lecture2 extends child{

	public static void main(String[] args) {
		child child = new child();
		child.method();

	}
}



결과
x	=> 10
this.x	=> 10
super.x	=> 10

 

 

 

 

 

object 클래스를 제외한 모든 클래스의 생성자 첫 줄에는

생성자 (같은 클래스의 다른 생성자 또는 조상의 생성자) 를 호출해야 한다.

생성자가 없다면 컴파일러가 자동적으로 기본 생성자( this() / super() )를 첫 줄에 만들어 준다.

 

고로, 자식 클래스에서 생성자를 만들어서 쓸 때,

부모의 생성자가 기본 생성자가 없을 시

자식 클래스 생성자 안에 부모 생성자를 먼저 선언해주어야 한다.

그렇지 않으면 컴파일 에러남.

 

아래의 예를 보자.

 

package object;

class Point{
	int x;
	int y;
	
	Point(int x, int y){
		this.x = x;
		this.y = y;
	}

	String getLocation() {
		return "x : " + x + ", y : " + y;
	}	
}

class Point3D extends Point{
	int z ;
	
	// 아래에 에러
	Point3D(int x, int y, int z){
		this.x = x;
		this.y = y;
		this.z = z;	
	}
	
	String getLocation() {
		return "x : " + x + ", y : " + y + ", z : " + z;
	}
}

 

위처럼 쓰게 되면 자식 클래스 생성자에서 

 

Implicit super constructor Point() is undefined. Must explicitly invoke another constructor

 

이와 같은 에러가 뜬다.

그래서 생성자를 추가를 해야하는데

 

	Point3D(int x, int y, int z){
		super(1,2);
        // 인수를 추가한 생성자를 선언 해준다면 해결된다.
        // 인수는 어차피 자식 클래스 선언 시 덮어씌워지니 상관이 없다
		this.x = x;
		this.y = y;
		this.z = z;	
	}

 

위와 같이 고치면 해결이 된다.

 

 

 

 

 


 

 

 

 

 

static

 

8/4 메모리 사용 방식과 같이 보길 바란다.

 

어떤 녀석을 어느 곳에서든 사용을 하고 싶을 때 사용하는 녀석이다.

static 으로 설정하면 같은 곳의 메모리 주소만을 바라보기 때문에

static 변수의 값을 모두 공유하게 된다.

또한, 자바는 static 을 설정할 때, 메모리 할당을 딱 한번만 하게 되어 메모리 사용에 이점이 있다.

 

 

 

다음 static 변수 예제를 보면서 이해해보자.

 

class Counter  {
    static int count = 0;	// static 이 붙음.
    Counter() {
        count++;  // static 전에는 원래는 this.count 였다.
        System.out.println(count);  // 마찬가지로 this.count 
    }
}

public class Sample {
    public static void main(String[] args) {
        Counter c1 = new Counter();
        Counter c2 = new Counter();
    }
}



결과
1
2

 

만약 this.count 였다면

c1과 c2는 다른 객체기 때문에 결과는 둘다 1, 1이 나왔을 것이다.

하지만 static으로 다 같은 주소를 가진 변수가 되었기 때문에

count의 증가가 c1, c2에서 각각 들어감으로써 값이 증가되는 걸 볼 수 있다.

 

이를 메소드에 붙이게 되면

역시 어디서든 사용할 수 있는 메소드가 될 수 있다.

다만, 해당 메소드 안에 변수가 있다면 그 변수도 static이 붙어 있어야 접근이 가능하다.

 

 

 

 

 


 

 

 

 

 

get / set 메소드

 

자바라는 언어는 기본적으로 데이터의 은닉이 장점인 언어이다.

private이라는 접근 제어자가 있기 때문인데 아래의 예처럼

외부에서의 접근을 막는 것에 탁월하다. 

 

public class Class{
	private int number;
}

public class Test {

	public static void main(String[] args) {
		
		Class a = new Class();
		a.number = 10;		// 접근을 못해서 error 발생
	}

}

 

 

 

 

 

하지만 이렇게 기껏 막아놓은 데이터도

값을 바꾸고 싶을 때가 있고 얻어내고 싶을 경우가 있다.

이런 걸 좀 돌려서 받아내고 싶을 때 사용하는 것이

get / set 메소드이다.

 

아래의 예처럼,

각 변수들은 private으로 설정하고

각 변수들의 값을 설정할 수 있는 set 이름을 붙인 메소드,

각 변수들의 값을 얻어올 수 있는 get 이름을 붙인 메소드를 만든다.

이후 메소드를 이용해서 값을 넣고 받아오면 해-결 

 

package object;

import java.util.Scanner;

class Time{
	private int hour;
	private int minute;
	private int second;
	
	public static Scanner typing = new Scanner(System.in);
	
	Time(int hour, int minute, int second){
		setHour(hour);
		setMinute(minute);
		setSecond(second);
	}
	
	public int getHour() {
		return hour;
	}
	public int getMinute() {
		return minute;
	}
	public int getSecond() {
		return second;
	}

	public void setHour(int hour) {
		if(0 > hour || hour > 23) {
			System.out.println("장난치지 말고 다시 입력해 ㅡㅡ");
			return;
		}
		this.hour = hour;			
	}
	
	public void setMinute(int minute) {
		if(0 > minute || minute > 59) {
			System.out.println("장난치지 말고 다시 입력해 ㅡㅡ");
			return;
		}
		this.minute = minute;			
	}
	
	public void setSecond(int second) {
		if(0 > second || second > 59) {
			System.out.println("장난치지 말고 다시 입력해 ㅡㅡ");
			return;
		}
		this.second = second;			
	}
	
	@Override
	public String toString() {
		return "시간 [hour=" + hour + ", minute=" + minute + ", second=" + second + "]";
	}

}


public class Lecture2 {

	public static void main(String[] args) {
		
		System.out.print("입력해라 시간분초 : ");
		int hour = Time.typing.nextInt();
		int minute = Time.typing.nextInt();
		int second = Time.typing.nextInt();
		
		Time t = new Time(hour, minute, second);
		
		System.out.println(t);
	}
}



결과
입력해라 시간분초 : 10
13
55
시간 [hour=10, minute=13, second=55]

 

 

 

 

 


 

 

 

 

 

다형성

 

하나의 객체가 여러 가지 타입을 가질 수 있는 것을 의미한다.

상속, 추상화와 더불어 객체 지향 프로그래밍을 구성하는 중요한 특징 중 하나.

 

이 다형성 때문에 자바에서는 부모 클래스 타입의 참조 변수로

자식 클래스 타입의 인스턴스를 참조할 수 있도록 하고 있다.

아래의 예를 보면서 이해해보자.

 

class Parent { ... }
class Child extends Parent { ... }



Parent pa = new Parent(); // 허용
Child ch = new Child();   // 허용
Parent pc = new Child();  // 허용
Child cp = new Parent();  // 오류 발생.

 

 

 

왜 이렇게 되는지 쉽게 이해를 해보자

오류가 발생한 구문을 보면

child를 참조변수로 만들어진 cp가 새로운 Parent로 만들어진 클래스라는 건데

상식적으로 Parent의 멤버가 child 보다 적기 때문에 에러가 난다.

참 쉽쥬?

 

다시 말해,

Child cp = new Parent();

이 구문 자체를 해석해보자면

Child 클래스의 변수들을 머리로 가져다 쓸 건데 그 몸체는 Parent로 쓸거라는 의미이다.

그런데 몸체보다 머리가 가지고 있는 변수가 더 많잖아?

머리 기준으로는 팔이 하나 더 있어야한다는건데 몸은 팔이 2개야.

그니까 에러가 난다는거다.

 

 

 

 

 


 

 

 

 

 

abstract

 

 

 

abstract 메소드

자식 클래스에서 반드시 오버라이딩해야만 사용할 수 있는 메소드.

자식 클래스에서 내가 정해준 해당 이름으로 쓰되

너가 하고 싶은대로 구현하라고 만든 것인데

이걸 사용하면 생산성 향상 및 배포가 용이해진다.

구조는 다음과 같다.

 

abstract 반환타입 메소드이름();

 

 

 

 

 

abstract 클래스

 

위의 메소드처럼 클래스에 추상을 추가하는 클래스.

추상 클래스는 상속을 통해 자식 클래스를 만들고,

만든 자식 클래스에서 모든 추상 메소드를 오버라이딩하고 나서야

비로소 자식 클래스의 인스턴스를 생성할 수 있게 된다.

대체 이런 헛짓을 왜하냐.... 싶을 텐데,

이것이 중요한 이유는 다형성을 활용할 수 있기 때문이다.

 

 

 

우선 구조는 아래와 같다.

 

abstract class 클래스이름 {

    ...

    abstract 반환타입 메소드이름();

    ...

}

 

 

 

 

 

아래의 예를 보면서 이걸 왜 쓰는구나 감을 잡아보자.

 

abstract class Animal { abstract void sound(); }
class Cat extends Animal { void sound() { System.out.println("냐옹!"); } }
class Dog extends Animal { void sound() { System.out.println("멍!"); } }


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

        // Animal a = new Animal(); // 추상 클래스는 인스턴스를 생성할 수 없음.

        Cat c = new Cat();
        Dog d = new Dog();

        c.sound();
        d.sound();
    }
}



결과
냐옹!
멍!

 

위처럼

자식 클래스 별로 기능은 같지만 다른 결과물을 내야할 때

추상 클래스를 사용하면 같은 메소드 이름으로 다른 결과물을 내기 용이하다. 

 

 

 

 

 

좀 더 복잡한 예를 보고 이해를 해보자

 

package object;

class Product{
	int Price;
	int bonusPoint;
	
	Product(int Price){
		this.Price = Price;
		bonusPoint = (int)(Price/10);
	}
}

class TV extends Product{
	TV(){
		super(100);	// 부모클래스 생성자 먼저 호출.
	}
	
	public String toString() {
		return "TV";
	}
}

class Notebook extends Product{
	Notebook(){
		super(150);	// 부모클래스 생성자 먼저 호출.
	}
	
	public String toString() {
		return "Notebook";
	}
}

class Buyer{
	int money = 1000;
	int bonusPoint = 0;
	
	void buy(Product p) {
		if(money < p.Price) {
			System.out.println("잔액이 모자라서 물건을 살 수가 없습니다.");
			return;
		}
		money	-= p.Price;
		bonusPoint += p.bonusPoint;
		System.out.println(p + " 를 구매했습니다.");
	}
}

public class Lecture2 {

	public static void main(String[] args) {
		Buyer b = new Buyer();
		TV	tv = new TV();
		Notebook nb = new Notebook();
		
		b.buy(tv);
		b.buy(nb);
		
		System.out.println("현재 남은 돈 : " + b.money + "만원 입니다.");
		System.out.println("현재 보너스 점수는 " + b.bonusPoint + "만 포인트 입니다.");
	}
}



결과
TV 를 구매했습니다.
Notebook 를 구매했습니다.
현재 남은 돈 : 750만원 입니다.
현재 보너스 점수는 25만 포인트 입니다.

 

이 예제에서 생성자 호출이 이해가 안된다면

맨 위의 super쪽을 다시 보고 올 것.

 

 

 

 

 


 

 

 

 

 

참조 변수의 타입 변환 (up-casting / down-casting)

 

1. 서로 상속 관계에 있는 클래스 사이에만 타입 변환을 할 수 있다.

2. 자식 클래스 타입에서 부모 클래스 타입으로의 타입 변환은 생략할 수 있다. (up-casting)

3. 하지만 부모 클래스 타입에서 자식 클래스 타입으로의 타입 변환은 반드시 명시해야 한다. (down-casting)

 

 

 

 

아래 예제로 이해 완료 끗.

 

class Parent { ... }
class Child extends Parent { ... }
class Brother extends Parent { ... }

...

Parent pa01 = null;
Child ch = new Child();
Parent pa02 = new Parent();
Brother br = null;

...

pa01 = ch;          
// pa01 = (Parent)ch; 와 같음. 타입 변환을 생략할 수 있음.
// up-casting

br = (Brother)pa02; 
// 위의 반대, 타입 변환을 생략 불가.
// down-casting

br = (Brother)ch;   // 직접적인 상속 관계가 아니므로, 오류 발생.

 

 

 

 

 

아래의 예제도 참고해보자.

 

package object;

class Car{
	String color;
	int door;


	void drive() {
		System.out.println("자동차가 움직임");
	}	
	
	void stop() {
		System.out.println("자동차가 멈춤");		
	}
}


class Sobangcha extends Car{
	void water() {
		System.out.println("불자동차가 물을 뿌립니다.");				
	}
	
}


public class Lecture  {
	
	public static void main(String[] args) {
		Car car = null;
		Sobangcha sc1 = new Sobangcha();
		Sobangcha sc2 = null;
		
		sc1.water();
		car = sc1; // up-casting : 형변환 생략 가능
		System.out.println("sc1 => " + sc1);
		System.out.println("car => " + car);
		//car.water(); 하지만 소방차 기능은 없음. => 컴파일 에러		
		
		
		
		sc2 = (Sobangcha)(car); // down-casting : 형변환 생략 불가
		sc2.water(); // 소방차 기능 있음 => 가능
	}
}



결과
불자동차가 물을 뿌립니다.
sc1 => object.Sobangcha@5305068a
car => object.Sobangcha@5305068a

 

 

 

 

 

※ 참고 - 반드시 기억해 둘 점

 

변수는 각자의 참조 클래스대로 따라가지만 

메서드는 항상 자식 클래스의 메서드를 따라간다.

 

아래의 예를 보자.

 

package object;

class Parent{
	int x = 100;
	
	void stop() {
		System.out.println("부모님이 멈춤");		
	}
}

class Child extends Parent{
	int x = 200;
	
	void stop() {
		System.out.println("x	=> " + x);		
		System.out.println("this.x	=> " + this.x);		
		System.out.println("super.x	=> " + super.x);		
	}
	
}

public class Lecture  {
	
	public static void main(String[] args) {
		Parent p = new Child();
		Child c = new Child();
		
		System.out.println("부모님 인자는 " + p.x);
		p.stop();
		System.out.println("자식 인자는 " + c.x);
		c.stop();
	}
}



결과
부모님 인자는 100
x	=> 200
this.x	=> 200
super.x	=> 100
자식 인자는 200
x	=> 200
this.x	=> 200
super.x	=> 100

 

 

 

 

 

 

 

 

728x90
반응형

댓글