객체 지향을 확장하기 위한 키워드

짱J·2023년 4월 2일
0
post-thumbnail

abstract 키워드

추상 메서드(abstract method)는 간단하게 선언부는 있는데 구현부가 없는 메서드를 말한다.
추상 메서드를 하나라도 갖고 있는 클래스는 반드시 추상 클래스로 선언해야 한다.

코드로 abstract 키워드가 필요한 이유에 대해 알아보자.

package abstractMethod01;

public class Driver01 {
	public static void main(String[] args) {
    	동물[] animals = new 동물[5];
    	animals[0] = new();
        animals[1] = new 고양이();
        animals[2] = new 강아지();
        animals[3] = new 송아지();
        animals[4] = new 병아리();
        
        for (int index = 0; index < animals.length; index ++) {
        	animals[index].cry();
        }
    }
}

이전 포스트 객체지향의 4대 특성에서 상속에 대해 설명하며 나온 코드를 조금 변형하였다.
쥐, 고양이, 강아지, 송아지, 병아리 클래스의 cry()는 각 동물의 울음소리에 맞게 출력된다고 가정하자. (찍찍, 야옹야옹 등등 ...)
코드가 너무 길어져 생략한다.

이 때 질문!

동물 클래스 인스턴스에서 cry()를 호출하면 어떤 울음소리를 내야 할까❓

동물 타입의 참조 변수를 통해 하위 클래스의 인스턴스가 가진 cry() 메서드를 호출하고 있으니, 상위 클래스인 동물의 cry() 메서드가 존재해야 한다.

이런 경우 필요한 것이 추상 메서드이다.

package abstractMethod02;

public abstract class 동물 {
	abstract void cry();
}
동물 짐승 = new 동물(); // Cannot instantuate the type 동물

이와 같이 추상 클래스는 인스턴스를 만들 수 없는 클래스가 된다.

package abstractMethod02;

public class 고양이 {
//	void cry() {
//    	System.out.println("야옹야옹");
//    }
}

// The type 고양이 must implement the inherited abstract mehtod 동물.cry()

이렇게 상위 클래스를 상속한 하위 클래스가 추상 메서드를 오버라이딩하지 않아도 컴파일 에러가 발생한다.

🐣정리🐣

  • 추상 클래스는 인스턴스(객체)를 만들 수 없다. new 사용 불가
  • 추상 메서드는 하위 클래스에게 오버라이딩(메서드의 구현)을 강제한다.
  • 추상 메서드를 포함한 클래스는 반드시 추상 클래스여야 한다.

생성자

반환값이 없고 클래스명과 같은 이름을 가진 메서드를 객체 생성자 메서드라고 한다.

개발자가 코드를 작성하지 않아도 컴파일 과정에서 자바 컴파일러가 알아서 기본 생성자를 만들어준다.
필요하다면 인자를 갖는 생성자를 추가로 만들 수 있다.

package constructor02;

public class 동물 {
	// 기본 생성자 메서드
	public 동물() {}
    
    public 동물(String name) {
    	System.out.println("name");
    }
}

💥 주의 💥
개발자가 생성자를 만들지 않으면 자바가 인자가 없는 기본 생성자를 자동으로 만들어주지만,
인자가 있는 생성자가 하나라도 있다면 자바는 기본 생성자를 만들어주지 않는다.


static 블록

static 블록은 클래스가 로딩되고 클래스 변수가 준비된 후 자동으로 실행되는 블록이다.
static 블록에서는 static 멤버만 사용할 수 있다.
클래스를 초기화할 때 꼭 수행되어야 할 작업이 있을 경우 유용하게 사용된다.

// 동물.java
package staticBlock;

public class 동물 {
	static {
    	System.out.println("Hello world");
    }
}

// Driver.java
package staticBlock;

public class Driver {
	public static void main(String[] args) {
    	동물 뽀로로 = new 동물();
    }
}

🧊 결과 🧊
Hello world

다른 예제들도 해보자.

package staticBlock;

public class Driver {
	public static void main(String[] args) {
    	System.out.println("main 메서드 시작");
    }
}

🧊 결과 🧊
main 메서드 시작

동물 클래스를 사용하지 않았으므로, 당연히 static 블록은 실행되지 않는다.

package staticBlock;

public class Driver {
	public static void main(String[] args) {
    	System.out.println("main 메서드 시작");
        동물 뽀로로 = new 동물();
    }
}

🧊 결과 🧊
main 메서드 시작
Hello world

동물 클래스보다 main() 메서드 실행문이 먼저 실행된다.

package staticBlock;

public class Driver {
	public static void main(String[] args) {
    	System.out.println("main 메서드 시작");
        동물 뽀로로 = new 동물();
        동물 핑구 = new 동물();
    }
}

🧊 결과 🧊
main 메서드 시작
Hello world

동물 클래스 인스턴스를 여러 개 만들어도 static 블록은 단 한번만 실행된다.

// 동물.java
package staticBlock;

public class 동물 {
	static int age = 0;
	static {
    	System.out.println("Hello world");
    }
}

// Driver.java
package staticBlock;

public class Driver {
	public static void main(String[] args) {
    	System.out.println("main 메서드 시작");
        System.out.println(Animal.age);
    }
}

🧊 결과 🧊
main 메서드 시작
Hello world
0

클래스 인스턴스를 만드는 작업뿐만 아니라 클래스의 정적 속성에 접근할 때도 static 블록이 실행된다.

🐣 정리 - static 블록이 실행되는 경우 🐣

  • 클래스의 정적 속성을 사용할 때
  • 클래스의 정적 메서드를 사용할 때
  • 클래스의 인스턴스를 최초롤 만들 때

final 키워드

final 키워드는 클래스, 변수, 메서드 앞에 붙일 수 있다.

final 키워드를

  • 클래스에 붙이면, 하위 클래스를 만들 수 없다. (상속을 허락하지 않는다)
  • 변수에 붙이면, 변경 불가능한 상수가 된다.
  • 메서드에 붙이면, 오버라이딩을 할 수 없다.

더 자세히 알고 싶다면 생각: Java에 왜 const(상수)가 없을까?를 참고하자. 잘 정리되어 있다 :)


instanceof 연산자

instanseof 연산자는 만들어진 객체가 특정 클래스의 인스턴스인지 물어보는 연산자로, 결과로 true 또는 false를 반환한다.

package instanceOf01;

class 동물 {}
class 조류 extneds 동물 {}
class 펭귄 extneds 조류 {}

public class Driver {
	public static void main(String[] args) {
    	동물 동물객체 = new 동물();
        조류 조류객체 = new 조류();
        펭귄 펭귄객체 = new 펭귄();
        
        System.out.println(동물객체 instanceof 동물); // true
        
        System.out.println(조류객체 instanceof 동물); // true
        System.out.println(조류객체 instanceof 조류); // true
        
        System.out.println(펭귄객체 instanceof 동물); // true
        System.out.println(펭귄객체 instanceof 조류); // true
        System.out.println(펭귄객체 instanceof 펭귄); // true
        
        System.out.println(펭귄객체 instanceof Object); // true
    }
}

객체를 생성하는 코드가

동물 동물객체 = new 동물();
동물 조류객체 = new 조류();
동물 펭귄객체 = new 펭귄();

로 바뀌어도 결과는 동일하다.

instanceof 연산자는 객체 참조 변수의 타입이 아닌 실제 객체의 타입으로 처리한다.

instanceof 연산자는 클래스들의 상속 관계뿐만 아니라 인터페이스의 구현 관계에서도 동일하게 적용된다.

packageinstanceOf02;

interface 날수있는 {}
class 박쥐 implements 날수있는 {}
class 참새 implements 날수있는 {}

public class Driver {
	public static void main(String[] args) {
    	날수있는 박쥐객체 = new 박쥐();
        날수있는 참새객체 = new 참새();
        
        System.out.println(박쥐객체 instanceof 날수있는); // true
        System.out.println(박쥐객체 instanceof 박쥐); // true
        
        System.out.println(참새객체 instanceof 날수있는); // true
        System.out.println(참새객체 instanceof 참새); // true
    }
}

package 키워드

package 키워드는 네임스페이스를 만들어주는 역할을 한다.

스마트폰을 예시로 들어보자.
그냥 스마트폰이면 누구의 폰인지 알 수 없지만, 홍길동.스마트폰, 김이박.스마트폰 이렇게 소유자명을 앞에 붙이면 누구의 스마트폰인지 구분할 수 있다.
이렇게 소유자 같은 역할을 하는 것이 패키지이다.


interface, implements 키워드

객체지향의 4대 특성에서 인터페이스에 대해 간단하게 다뤄보았다.

인터페이스는 추상 메서드와 정적 상수만 가질 수 있기에 따로 메서드와 public과 abstract, 속성에는 public과 static, final을 붙이지 않아도 자동으로 붙여준다.

💥 자바 8부터는 디폴드 메서드라고 하는 객체 구상 메서드와 정적 추상 메서드도 지원한다. 💥

package Interface;

interface Speakable {
	double PI = 3.14159;
    final double absoluteZeroPoint = -275.15;
    
    void sayYes();
}

class Speaker implements Speakable {
	public void sayYes() {
    	System.out.println("I say No!");
    }
}

public class Driver {
	public static void main(String[] args) {
    	System.out.println(Speakable.PI);
        System.out.println(Speakable.absoluteZeroPoint);
        
        Speaker reporter1 = new Speaker();
        reporter1.sayYes();
    }
}

추상 클래스 vs 인터페이스

공통점

  • 가지고 있는 추상 메서드를 구현하도록 강제하며, 인스턴스화가 불가능하다.

인터페이스 추가 설명

  • 인터페이스는 일종의 추상 클래스로, 추상 메서드를 갖지만 추상 클래스보다 추상화 정도가 높다.
    • 추상 클래스와 달리 몸통을 갖춘 일반 메서드, 멤버 변수를 가질 수 없다.
  • 인터페이스는 인터페이스로부터만 상속받을 수 있으며, 클래스와 달리 다중상속이 가능하다.

사용 의도

  • 추상 클래스는 상속할 객체의 공통점을 찾아 추상화시킨 것이다.
    • 상속 관계를 타고 올라갔을 때 같은 부모 클래스를 상속하며 부모 클래스가 가진 기능들을 구현해야 할 경우 사용한다.
  • 인터페이스는 상속 관계를 타고 올라갔을 때 다른 조상 클래스를 상속하더라도 같은 기능이 필요할 경우 사용한다.

적절한 사용

  • 추상 클래스
    • 관련성이 높은 클래스 간 코드를 공유하고 싶은 경우
    • 추상 클래스를 상속 받을 클래스들이 공통으로 가지는 메소드와 필드가 많거나, public 이외의 접근자(protected, private) 선언이 필요한 경우
    • non-static, non-final 필드 선언이 필요할 경우
  • 인터페이스
    • 서로 관련성이 없는 클래스들이 인터페이스를 구현하게 되는 경우
      ex)Comparable, Cloneable 인터페이스는 여러 클래스들에서 구현되는데, 구현 클래스들 간에 관련성이 없다.
    • 특정 데이터 타입의 행동을 명시하고 싶은데, 어디서 그 행동이 구현되는지는 신경쓰지 않는 경우
    • 다중상속을 허용하고 싶은 경우

extends vs implements

extends는 클래스를 확장하는 것이고,
implements는 인터페이스를 구현하는 것이다.

  • extends는 일반 클래스와 abstract 클래스 상속에 사용되고, implements는 interface 상속에 사용된다.

  • class가 class 상속 받을 때, 인터페이스가 인터페이스 상속 받을 때는 extends
  • class가 interface 사용할 때는 implements
  • interface가 class 사용할 때는 implements 사용 불가
    • interfaces는 구현 목적!

  • extends는 클래스 1개만 상속 받을 수 있으며 부모 클래스의 기능을 사용
  • implements는 다중 상속이 가능하며 설계 목적으로 구현
  • implements한 클래스는 implementsdml 내용을 다 사용해야 함
    • 부모 메서드를 반드시 재정의해야 함!

this 키워드

this 키워드는 객체가 자기 자신을 지칭할 때 쓰는 키워드이다.

package This;

class 펭귄 {
	int var = 10;
    
    void test() {
    	int var = 20;
        
        System.out.println(var);
        System.out.println(this.var);
    }
}

public class Driver {
	public static void main(String[] args) {
    	펭귄 뽀로로 = new 펭귄();
        뽀로로.test();
    }
}

var 변수가 지역 변수도 있고, 객체 변수도 있다.

이 때 9번째 줄에서는 20(지역변수)이 출력되고, 10번째 줄에서는 10(객체변수)이 출력된다.

🐣 정리 🐣

  • 지역 변수와 속성(객체 변수, 정적 변수)의 이름이 같을 경우 지역 변수가 우선한다.
  • 객체 변수와 이름이 같은 지역 변수가 있을 경우, 객체 변수를 사용하려면 this를 사용한다.
  • 정적 변수와 이름이 같은 지역 변수가 있을 경우, 정적 변수를 사용하려면 클래스명.정적변수를 사용한다.

super 키워드

super 키워드는 바로 위 상위 클래스의 인스턴스를 지칭하는 키워드이다.

package Super;

class 동물 {
	void method() {
    	System.out.println("동물");
    }
}

class 조류 extends 동물 {
	void method() {
    	super.method();
        System.out.println("조류");
    }
}

class 펭귄 extends 조류 {
	void method() {
    	super.method();
        System.out.println("펭귄");
        
        // super.super.method();
    }
}

public class Driver {
	public static void main(String[] args) {
    	펭귄 뽀로로 = new 펭귄();
        뽀로로.method();
    }
}

11번째 줄이나 18번째 줄처럼 super 키워드로 상위 클래스의 인스턴스 메서드를 호출할 수 있지만,
주석처럼 상위의 상위 클래스의 인스턴스에는 접근이 불가능하다.


☘️ Reference

profile
[~2023.04] 블로그 이전했습니다 ㅎㅎ https://leeeeeyeon-dev.tistory.com/

0개의 댓글