인터페이스(Interface)

 

- 추상 메소드들로만 이루어진 형태로, 객체의 사용 방법을 정의한 새로운 타입이다.

 

- 상수 외에 필드를 선언할 수 없다.

 

- 자바에서 인터페이스란 객체와 객체 사이의 상호 작용이 이루어지는 인터페이스이다.

 

- 추상 메소드의 성질을 이용하면, 인터페이스는 여러 객체들과 사용이 가능하므로 어떤 객체를 사용하느냐에 따라 실행 내용과 리턴값이 다를 수 있다.

 

- , 개발 코드를 수정하지 않고 사용하는 객체만 변경하여 메소드를 실행한다.

 

- 객체의 교환성을 높여주기 때문에 다형성을 구현하는 매우 중요한 역할을 한다.

 

상속은 같은 종류의 하위 클래스를 만드는 기술이고, 인터페이스는 사용 방법이 동일한 클래스를 만든다는 점에서 개념적인 차이가 있다. 그러나 둘 다 다형성을 구현하는 기술이다.


- 인터페이스를 구현하는 객체를 구현 객체라고 부르며, 구현 객체를 생성하는 클래스를 구현 클래스라고 부른다. 구현 클래스는 추상 메소드의 몸체를 반드시 구현하여야 한다.

 

- 인터페이스의 모든 메소드는 기본적으로 public 접근 제한자를 갖기 때문에 구현 클래스에서 작성할 때 public 보다 더 강한 접근 제한자(default, private )를 사용할 수 없다. , public을 생략해선 안된다.

 

- 상속과는 달리 구현 클래스는 다수의 인터페이스를 구현할 수 있다.

 

- 인터페이스끼리는 상속이 가능하다.

public interface 인터페이스1 extends 인터페이스2 { ... }

 

- 구현 클래스는 선언부에 implements 키워드를 추가하고 인터페이스 명을 명시해야 한다.

* implements 시행하다, 도구라는 뜻이다.

 

- 인터페이스 타입의 변수는 참조형 변수이기 때문에 구현 객체가 대입될 경우 구현 객체의 번지를 저장한다.


 


인터페이스의 구성 멤버

 

- 상수 필드 : 상수는 인터페이스에 고정된 값이므로 런타임 시에 데이터를 바꿀 수 없다. 상수를 선언할 때에는 반드시 초기값을 대입해야 한다.


public static final 자료형 변수명;


 

- 추상 메소드 : 객체가 사용할 메소드의 형태를 설명한 것으로, 어ᄄᅠᆫ 매개값이 필요하고 리턴 타입이 무엇인지만 알려준다. 실체 실행부는 해당 인터페이스를 구현하는 클래스에서 작성해야 한다.

 

- 디폴트 메소드 : 자바 8버전에서만 허용하는 멤버이며, 기존 인터페이스를 확장해서 사용하기 위함이다. 선언할 때 인터페이스를 구현한 실제 객체가 가지고 있는 메소드라고 생각하면 된다. 따라서 구현 객체에서는 인터페이스의 디폴트 메소드의 실체 메소드를 작성할 필요가 없다.

 

- 정적 메소드 : 자바 8버전에서만 허용하는 멤버이며, 디폴트 메소드와 달리 객체가 없어도 인터페이스만으로 호출이 가능하다. 쉽게 말해, 인터페이스를 선언하지 않고 "인터페이스이름.정적메소드명()"과 같이 바로 호출이 가능함.

 

 

 

인터페이스의 일반적인 형태

 

- 인터페이스의 모든 메소드는 추상 메소드로 간주되므로 abstract키워드는 사용하지 않는다.

 


public interface 인터페이스명 {

//상수

...

 

//추상 메소드

리턴타입 추상메소드1(...);

리턴타입 추상메소드2(...);

...


//디폴트 메소드 -> default를 붙여야한다. 안 붙이면 추상 메소드로 간주됨

default 리턴타입 메소드명(...) { ... }

...


//정적 메소드

static 리턴타입 메소드명(...) { ... }

}




public class 클래스명 implements 인터페이스명 {

 

//FIELDS

...

 

//CONSTRUCTOR

...

 

//METHODS

@Override

리턴타입 추상메소드1(...) {

    ...

}


@Override

리턴타입 추상메소드2(...) {

    ...

}

 

} 


- @Override 란 인터페이스의 추상 메소드에 대한 정확한 실체 메소드인지 컴파일러가 체크하도록 지시하는 어노테이션이다. 생략해도 상관없으나, 실체 메소드가 잘못되지 않도록 해주기 때문에 붙여 주는 것이 좋다.




▶ 인터페이스의 타입 변환


- 구현 객체가 인터페이스 타입으로 변환되는 것은 자동 타입 변환에 해당한다. 이렇게 자동 타입 변환된 구현 객체는 해당 클래스의 필드와 메소드를 사용할 수 없다. 즉, 인터페이스의 상수와 메소드만 사용가능하다.


- 반대로, 인터페이스 타입인 구현 객체를 강제 타입 변환하여 구현 클래스 타입으로 사용할 수 있다.


- 필드와 매개변수의 타입을 인터페이스로 선언하면 여러 구현 객체를 대입하여 다양한 실행 결과를 만들 수 있다.


 

 

배열(Array)

 

- 배열(Array)은 같은 자료형(type)의 변수들을 순서가 있는 데이터로 저장할 수 있는 자료구조이다.

 

- 배열은 객체이기 때문에 초기화한 배열의 크기만큼 메모리 공간을 할당받는다.

 

- 인덱스(index)라고 하는 원소번호를 통해 저장된 데이터를 관리하며, 인덱스를 이용하여 배열에 있는 원소들에 접근한다.

 

- 배열의 크기를 초기화하면 수정할 수 없기 때문에 사용하지 않는 배열 공간 때문에 메모리 공간이 낭비되기도 한다.

 

- 배열의 첫 번째 인덱스 번호는 0번부터 시작한다.

 

- 배열의 원소는 변수이며, 기초형 및 참조형 모두 저장할 수 있다.

 

 

 

배열의 생성

 

- 자바에서 배열은 객체이므로 다른 객체들을 선언하는 것과 유사하다.

 

- 자바에서는 초기화된 변수로 배열의 크기를 지정할 수 있다. (C에서는 안됨)

 

- 배열의 크기는 임의로 정할 수 있으나, 선언 시 배열의 크기를 정해선 안 된다.

< 잘못된 예시 >

int matrix[5];  (X)

int matrix[5] = { 1, 2, 3, 4, 5 };  (X)

 

- 선언 후 생성 (두 가지 방법 중 편한 대로 하면 됨.)


int[] numbers; 또는 int numbers[]; //정수형 배열 선언

numbers = new int[5]; //배열 객체 생성 



- 선언과 동시에 생성 후 초기화


int[] numbers = new int[5]; //정수 타입의 데이터가 들어가며, 5개의 원소를 가지는 배열


numbers = { 10, 20, 30, 40, 50 }; 


float[] fs = new float[20]; //실수 타입의 데이터가 들어가며, 20개의 원소를 가지는 배열

char[] cs = new char[30]; //문자 타입의 데이터가 들어가며, 30개의 원소를 가지는 배열  



- 선언과 동시에 초기화 : 배열을 따로 생성하지 않아도 초기화한 데이터의 개수만큼 배열의 크기가 자동으로 정해진다. 


int[] numbers = { 10, 20, 30 };  



- 배열의 크기는 (자료형 바이트 * 원소의 개수) 이다. 5개의 원소를 가진 정수형 배열은 4byte * 5 = 20byte의 메모리 공간이 할당된 것이다.

 

 

 

for-each 루프 (향상된 for)

 

- 형식

for (자료형 변수명 : 배열이름)

{

    //반복 문장들

}

 

- 해당 배열의 인덱스 0번부터 마지막 배열 원소까지 차례대로 왼쪽 변수에 복사해서 for문을 실행한다.

 

- 예시 


int[] numbers = new int[5];

 

for(int value : numbers)

{

    System.out.println("value : " + value);

} 


 

- 결과1 (정수 타입의 데이터는 초기화하지 않으면 디폴트값으로 0이 저장된다.); 


value : 0

value : 0

value : 0

value : 0

value : 0  



- 결과2 ( numbers = { 10, 20, 30, 40, 50 };을 추가했을 때) 


value : 10

value : 20

value : 30

value : 40

value : 50 



 

 

배열의 활용


[ 위 1차원 배열, 아래 2차원 배열 ]

 

- 매개변수로 배열 원소가 전달되면 복사된 값이 호출한 메소드에서 사용된다.

 

- 매개변수로 배열 전체가 전달되면 배열도 객체이기 때문에 해당 배열의 주소를 가리키는 참조값이 전달된다.

 

- 배열의 원소가 참조형 변수일 경우, 배열의 원소에는 주소를 카리키는 참조값이 저장되어 있다. 예를 들어, 객체들의 배열일 경우 배열의 원소에는 객체가 있는 것이 아니라 객체를 가리키는 객체의 주소가 들어있는 것이다.

 

- 2차원 배열 : 같은 행의 원소들을 중괄호로 묶는다. 


int[][] numbers = new int[3][5];

numbers = { {1, 2, 3, 4, 5}, {10, 20, 30, 40, 50}, {100, 200, 300, 400, 500} }; 



또는

  


int[][] numbers = { {1, 2, 3, 4, 5}, {10, 20, 30, 40, 50}, {100, 200, 300, 400, 500} }; 


 

< int[][] numbers = new int[n][m]; 일 때

 n개의 1차원 배열이 m개의 1차원 배열을 가리키는 형태 >



- 다차원 배열 : 2차원 배열과 유사하며, 주로 회사의 매출액을 지역별 부서별 월별로 분류하는 것과 같은 상황에서 사용한다.


int[][][] numbers = new int[2][3][4]; 


 









 추상 클래스


 - 객체를 직접 생성할 수 있는 클래스를 실체 클래스라고 하는 반면, 이 실체 클래스들의 공통적인 속성(필드와 메소드)만 뽑아내어 선언한 클래스를 추상 클래스라 한다.


 - 추상 클래스는 new 연산자를 사용하여 객체를 생성할 수 없다.

 

 - 추상 클래스와 실체 클래스는 상속의 관계에 놓여있다.


 - 추상 클래스가 부모이고 실체 클래스가 자식으로 구현되며, 실체 클래스는 추상 클래스에 있는 속성 외에 추가적인 속성을 가질 수 있다.


 - 추상 클래스는 새로운 실체 클래스를 위한 부모 클래스의 용도로만 사용된다.



 추상 클래스의 장점


 - 실체 클래스들의 필드와 메소드를 통일하여 실체 클래스 작성 시 시간을 절약할 수 있다.



▶ 추상 클래스의 선언



public abstract class 클래스명 {

//필드

//생성자

//메소드

}




 추상 메소드와 오버라이딩(Overriding)


 - 추상 메소드란 메소드의 선언부만 있고 메소드 실행 내용인 중괄호 {}가 없는 메소드를  말한다. 끝에는 항상 세미콜론이 있다.


 - 추상 메소드는 추상 클래스에서만 선언할 수 있다.


 - 메소드 실행 내용인 중괄호 블록은 추상 메소드를 상속받은 실체 메소드에서 직접 작성한다.


 - 반드시 자식 클래스는 추상 메소드를 재정의(Overriding)해서 실행 내용을 작성해야 한다.


 - 즉, 추상 클래스를 설계할 때, 하위(자식) 클래스가 반드시 실행해야 하는 메소드를 추상 메소드로 선언하면 된다.



public abstract class Car{

//필드

public int speed;

public String owner;


//생성자

public Car(String owner) {

this.owner = owner;

}


//메소드

public abstract void accelerate(int speed);

public abstract void whatKind();


public void driving() { //모든 자식 객체가 실행하는 메소드

System.out.println("운전 중");

}


}




public class SportCar extends Car{

//필드

int speed;


//생성자

public SportCar(String owner, int speed) {

super(owner);


this.speed = speed;

}


//메소드

@Override

public void accelerate(int speed) {

if(speed > 0) {

this.speed += ( 2 * speed );

}

}


@Override

public void whatKind() {

System.out.println("스포츠카입니다.");

}

}




public class Truck extends Car {

//필드

int speed;


//생성자

public SportCar(String owner, int speed) {

super(owner);


this.speed = speed;

}


//메소드

@Override

public void accelerate(int speed) {

if(speed > 0) {

this.speed += speed;

}

}


@Override

public void whatKind() {

System.out.println("트럭입니다.");

}

}




public class CarTest { //실행 클래스, 객체의 속성을 정의하는 클래스와는 별도로 생성하는 것이 좋다.

public static void main(String[] args) {

SportCar sportCar = new SportCar();

Truck truck = new Truck();


sportCar.whatKind();

truck.whatKind();


sportCar.driving();

truck.driving();


//변수의 자동 타입 변환

Car car = null;

car = new SportCar();

car.whatKind();

car = new Truck();

car.whatKind();

}

}



 

 

* 상속


- 자바에서 상속이란 부모 클래스의 멤버를 자식 클래스에게 물려주는 행위이다.


- 프로그램에서는 자식 클래스가 부모 클래스를 선택하며 클래스명 뒤에 “extends 부모 클래스명를 기술하여 사용한다.


- , 다중 상속은 허용하지 않는다.

 

class 자식클래스명 extends 부모클래스1, 부모클래스2 --> 절대 X

 

- 다른 패키지의 클래스를 상속할 경우 패키지명.클래스명을 import해야한다.

상속의 장점


- 상속은 이미 개발된 클래스를 재사용하여 새로운 클래스를 만들기 때문에 코드의 중복을 줄여준다.


- 상속을 이용하여 클래스의 수정을 최소화할 수 있다. , 유지 보수 시간을 최소화한다.


package sec.example;

 

public class A {

int field1;

 

void method1() { ... }    

}

 

public class B extends A { //A를 상속함

String field2;

 

void method2() { ... }

}



 

상속 가능 범위


- 부모 클래스에서 private 접근 제한을 갖는 멤버들(필드와 메소드)은 상속 대상에서 제외된다.


- 부모 클래스와 자식 클래스가 서로 다른 패키지일 때 부모 클래스의 default 접근 제한을 갖는 멤버들도 상속 대상에서 제외된다.


- 그 외에 상속한 클래스의 필드와 메소드는 자식 클래스에서 자유롭게 사용할 수 있다.


B b = new B();

//A로부터 물려받은 필드와 메소드

b.field1 = 10;

b.method1();

 

//B가 추가한 필드와 메소드

b.field2 = "홍길동“;

b.method2();



 

* 부모 생성자의 호출


- 자식 객체를 생성하면, 부모 객체가 먼저 생성되고 그 다음에 자식 객체가 생성된다. 마치, 부모 없는 자식이 없는 것처럼 말이다.


- 부모 생성자는 자식 생성자의 맨 첫줄에서 super() 키워드를 통해 호출된다. 생성자가 명시적으로 선언되지 않았다면 컴파일러는 디폴트 생성자에 super(); 키워드를 맨 앞에 추가하여 생성해 낸다. 여기서 super(매개변수); 는 부모 생성자 역할을 한다.


- super의 매개변수는 부모 생성자의 매개변수의 형태와 동일해야한다.

 

 

* 메소드의 재정의(Overriding)


- 어떤 메소드는 상속받은 자식 클래스에서 사용하기에 부적합할 수도 있다. 이 문제를 해결하고자 상속된 일부 메소드는 자식 클래스에서 다시 수정해서 사용할 수 있다. , 자식 클래스에서 부모 클래스의 메소드와 동일한 메소드를 재정의하는 것을 말한다.


- 재정의할 메소드 위에는 @Override 키워드가 있어야 한다.


- 메소드가 오버라이딩되면 부모 객체의 메소드는 숨겨지기 때문에 자식 객체에서 메소드를 호출하면 오버라이딩된 자식 객체가 호출된다.


- 그러나 간혹, 숨겨진 부모 객체의 메소드를 사용해야 할 때가 있다면, super 키워드를 이용하여 호출할 수 있다.


 super.부모메소드();



- 메소드 오버라이딩 규칙

  • 부모의 메소드와 동일한 구조(리턴 타입, 메소드 이름, 매개 변수 리스트)를 가져야 한다.
  • 접근 제한을 더욱 강하게 오버라이딩 할 수 없다.
  • (상속된 부모 객체의 메소드가 default일 때 private로 바꿀 수 없으며, public으로는 가능하다.)
  • 새로운 예외(Exception)throws 할 수 없다.

 


final 클래스와 final 메소드


- 상속할 수 없는 final 클래스 : 클래스 선언 시 아래와 같이 class 앞에 final 키워드를 붙이게 되면 이 클래스는 최종적인 클래스이므로 상속할 수 없는 클래스가 된다. , final 클래스는 부모 클래스가 될 수 없다.


public final class 클래스명 { ... }



- 오버라이딩(Overriding)할 수 없는 final 메소드 : 메소드를 선언할 때 final 키워드를 붙이게 되면 이 메소드는 최종적인 메소드가 되므로 오버라이딩할 수 없는 메소드가 된다. , 부모 클래스에서 상속해서 자식 클래스를 선언할 때 부모 클래스에 선언된 final 메소드는 자식 클래스에서 재정의할 수 없다.


public final 리턴타입 메소드(매개변수) { ... } //해당 메소드 재정의 불가




* protected 접근 제한자


- 부모 클래스의 멤버가 protected 접근 제한자를 가질 경우 자식 클래스는 다른 패키지라도 부모 객체의 필드, 생성자, 메소드를 사용할 수 있다.


- 생성자의 경우, 부모 객체를 new 연산자로 생성하는 것이 아니라 자식 클래스의 생성자에서 super(); 키워드를 통해서 호출할 수 있다.

 

 

* 타입 변환과 다형성


- 다형성은 하나의 타입에 여러 객체를 대입하여 이용할 수 있는 성질을 말한다. 이러한 다형성을 위해 자바는 자식 클래스에서 부모 클래스로 자동 타입 변환하는 것을 허용한다. , 부모 타입에는 모든 자식 객체가 대입될 수 있다.

 

만약 Car 클래스를 상속하는 SportCar 클래스가 있다면,


SportCar mySportCar = new SportCar();

Car myCar = mySportCar; //자동 타입 변환


mySportCar 변수와 myCar 변수는 타입만 다를 뿐 동일한 객체(SportCar)를 참조한다.


- 매개 변수의 타입이 클래스일 경우, 해당 클래스의 객체 외에 상속받는 자식 객체까지도 매개값으로 사용할 수 있다.


- , 부모 타입으로 자동 타입 변환된 이후에는 부모 클래스에 선언된 필드와 메소드만 접근할 수 있다. , 변수는 자식 객체를 참조하더라도 해당 변수로 접근 가능한 멤버는 부모 클래스의 멤버로 제한된다.


- 그러나 메소드가 자식 클래스에서 오버라이딩 되었다면 부모 타입으로 자동 타입 변환되어도 오버라이딩된 메소드가 호출된다.


- 바로 위의 부모 클래스가 아니더라도 상속 계층에서 상위 타입이라면 하위 타입이 자동 타입변환 된다.

- 강제 타입 변환(Casting) : 자식 타입이 부모 타입으로 자동 변환한 후, 다시 자식타입으로 변환할 때 강제 타입 변환을 해야 한다. 즉, 부모 타입에 부모 객체가 아닌 자식 객체가 있는 경우에 가능하다.

 

자식클래스 변수 = (자식클래스) 부모클래스;

 

 

* 객체 타입 확인


- 강제 타입 변환은 자식 타입이 부모 타입으로 변환되어 있는 상태에서만 가능하므로 부모 타입의 변수가 부모 객체를 참조할 경우 자식 타입으로 변환할 수 없다.


- instanceof : 위의 문제점을 해결하고자 어떤 객체가 어떤 클래스의 인스턴스(객체)인지 확인하는데 사용되는 연산자이다. 


boolean result = 좌항(객체) instanceof 우항(타입);



- 해당 타입이면 true, 아니면 false를 리턴한다.


// Child extends Parent

// Child -> Parent (자동타입변환)

// Parent -> Child (캐스팅)

 

public void method(Parent parent) {

Child child1 = new Child();

Parent parent = child1;


//parent 변수가 참조하는 객체는 Child 클래스의 인스턴스(객체)인가?

if(parent instanceof Child) {

    Child child = (Child) parent

    }

}




 

+ Recent posts