[TIL]20210911

박창현·2021년 9월 11일
0

TODAY I LEARNED

목록 보기
45/53

자바

Getter, Setter 메소드를 이용해 필드를 보호하기.

객체지향 프로그래밍에서 객체의 필드를 객체 외부에서 직접적으로 접근하는 것을 막는다. 객체의 무결성을 보호하기 위해서이다. (자동차는 음수를 가질 수 없는데, 외부에서 임의로 음수가 되도록 조작한다던가 등)
이를 위해 객체의 필드 값을 메소드를 통해 조작하는 것을 선호한다.
그리고 이앞에 Setter 메소드임을 알리기위해 set을 붙인다.

void setSepeed(double speed){
    if(speed < 0) {
        this.speed = 0;
        return;
} else {
    this.speed = speed;
}

또한, Getter 메소드도 Setter 메소드를 만들면서 private로 설정된 필드를 접근하기위해, 아니면 클래스 외부에 전달될 때 환산된 값을 보내줘야할 경우 사용한다.

double getSpeed(){
    double km = speed*1.6;
    return km;
}

참고로 boolean일 경우 get 대신 is 를 앞에 붙인다.

상속.

기존의 클래스를 재활용한다.
만약 SportsCar라는 클래스를 만들때 기존의 Car 클래스를 상속해온다면 굳이 새롭게 패달, 브레이크등의 코드를 새롭게 짤 필요가 없어진다.

class SportsCar extends Car { ... }

하지만
1. 다중상속 (extends Car, Car2)은 불가능하다.
2. 부모Class의 private도 상속되지 않는다. (default는 가능)

부모 생성자 호출

자식 객체가 생성되면 자동으로 부모 객체도 만들어진다. (내부적으로 만들어지는 순서는 부모 객체->자식 객체 순)
객체는 실행될 때 생성자가 필요한데, 부모 객체 또한 만들어진다.

자식 객체가 생성자를 명시하지 않는다면

(기본 생성자를 사용하기위해)

public 자식_생성자(){
    super(); // 부모의 기본 생성자 호출.
}

이런 식으로 알아서 구성된다.

만약 직접 자식 생성자를 선언한다면

public 자식_생성자(매개변수1, 매개변수2){
    super(매개값1, 매개값2);
}

이런식으로 구성한다.

예제

//부모 클래스
public class People{
    public String name;
    public String ssn;
    
public People(String name, String ssn){
    this.name=name;
    this.snn=ssn;
    }
}

//자식 클래스
public class Studnet extends People{
    public int studentNo;
    
    public Student(String name, String ssn, int studentNo){
    super(name, ssn);
    this.studentNo=studentNo;
    }
}

부모 클래스에는 기본 생성자가 없고 매개값을 받는 생성자만 있다.
그렇기에 자식 클래스에서는 super(name,ssn)으로 생성자를 호출해야만 한다.
부모 생성자를 호출할때는 무조건 super를 사용한다.
부모 클래스 이름이 People이라고 자식 클래스 생성자 밑에 그걸 적는 게 절대 아니다! 부모 생성자를 호출하는 기능은 항상 super()이고, 괄호에 매개변수가 들어가는지, 들어간다면 몇 개가 들어가는지만 적어주면 된다.

즉, 부모의 기본 생성자를 호출할때는 super()을 적는 건 마음대로이고,
매개변수가 있는 부모의 생성자를 호출할때는 super(매개변수1, 2, ...)를 통해 호출한다.

오버라이딩(메소드 재정의)

자식클래스에서 부모클래스의 일부 메소드를 다시 정의하는 것.
재정의할 때 주의해야할 점.
1. 부모의 메소드와 동일한 시그니처(리턴 타입, 메소드 이름, 매개 변수 목록)를 가져야한다. (어찌보면 당연. 하나라도 틀리다면 다른 메소드가 될테니.)
2. 접근 제한을 더 강하게 재정의할 수 없다.
3. 새로운 예외를 throws할 수 없다는데 아직 안배움.

여기서 2번 문항을 자세히 보자면
부모 메소드가 public 접근 제한을 가지고 있다면 이를 오버라이딩할때 default나 private처럼 더 강한 접근 제한으로 변경이 불가능하지만, 반대로 강한 접근 제한에서 public등 약한 접근 제한으로 수정은 가능하다.

오버라이딩 후, 만약 재정의 되지않은 기존의 부모 메소드를 쓰고 싶은 경우 super.부모메서드를 통해 호출이 가능하다.

만약, 부모와 자식 클래스에 A 메소드가 공동으로 있고, 오버라이딩 되었다면, 자식 클래스의 A 메소드안에서 if문을 통해 참이면 어떤 값을 실행하거고 거짓이면 super.A를 통해 부모 클래스의 A메소드를 실행하도록 만드는 것도 가능하다.

final 을 붙이면 상속이 불가능하다. 당연히 재정의도 안된다.

protected 접근 제한자

이는 같은 패키지 내에서는 public처럼 마음대로 쓸 수 있지만, 다른 패키지에서는 new를 이용한 일반적인 접근은 불가능 하고 무조건 extends를 통해 가능하다. 하지만 여기에도 제약이 있는데 상속으로 이용할 때에도 new를 쓸 수는 없고 super 를 무조건 이용해야 한다. 그러면 this. 을 통해 필드와 메소드의 접근이 가능해진다.

클래스 생성자에서 메소드 실행법.

문제를 보다가 알게됬는데,

생성자에서 this("대한민국"); 처럼 this를 이용해 자신의 메소드를 실행할 수 있다.

다형성

사용 방법은 동일(동일한 필드나 메소드)하지만 다양한 실행결과가 나오도록 만드는 성질

클래스 간의 자동 타입 변환

자식 클래스가 부모 클래스의 타입으로 변하는 것은 가능하지만, 부모 클래스에서 선언된 필드와 메소드만 접근이 가능하다. 부모의 타입으로 작동하지만 참조하는 객체는 원래의 자식 객체가 맞다. 하지만 타입 자체에는 자식만 가지고 있는 필드 메소드가 없기에 접근이 안된다. 단, 메소드가 자식 클래스에서 재정의(오버라이딩)되었다면 자식이 가지고이있는 메소드가 호출된다.
반대로, 부모 클래스가 자식 클래스의 타입으로 변하는 것은 불가능.

필드의 다형성

Tire.frontLeftTire = new Tire();
// 를
Tire.frontLeftTire = new HankookTire();
// 로 교체가 가능하다. (내부에 물론 같은 메서드 등만이 사용가능하지만)

매개 변수의 다양성

말로 설명하는 게 어려워 책의 코드를 그대로 가져왔다.

public class Vehicle {
  public void run(){
    System.out.println("차량이 달린다");
  }
}
public class Driver{
  public void drive(Vehicle vehicle){
  vehicle.run();
  }
}
public class Bus extends Vehicle {
  @Override
  public void run(){
    System.out.println("버스가 달린다.");
  }
}
public class Taxi extends Vehicle {
  @Override
  public void run(){
    System.out.println("택시가 달린다.");
  }
}
public class DriverExample(String[] args){
  Driver driver = new Driver();
  Bus bus = new Bus();
  Taxi taxi = new Taxi();
  
  driver.drive(bus); // 자동 타입 변환 Vehicle vehicle = bus;
  driver.dirve(taxi);// 자동 타입 변환 Vehicle vehicle = taxi;
}

위 코드를 보면 bus나 taxi에 추가되는 필드나 메소드가 없어 모든 코드들이 정상적으로 작동한다. 또한, 위에서 말한것 처럼 (자식 메소드에서 생긴 필드와 메소드는 못쓰는 것과 다르게) 예외적으로 오버라이드 된 메소드는 부모객체에서도 여전히 오버라이드된 상태로 작동하기때문에 버스가 달린다., 택시가 달린다. 등이 출력된다.

강제 타입 변환

강제 타입 변환은 부모 타입을 자식 타입으로 변환하는 것. 단, 자식 타입이 부모 타입으로 자동 타입 변환한 후 다시 자식 타입으로 변환할 때 강제 타입 변환을 사용할 수 있습니다.

자식타입 변수 = (자식타입) 부모타입;

Parent parent = new Child(); // 자동 타입 변환
Child child = (Child) parent; // 강제 타입 변환

객체 타입 확인 (instanceof 연산자)

좌항(객체) instanceof 우항(타입)
좌항의 객체가 우항의 인스턴스 즉, 우항의 타입으로 객체가 생성되었다면 ture이고, 아니면 false이다. (타입 변환되었다면, 모든 변환 전 오리지널의 타입을 뜻함.)

이를 이용해서 강제 타입 변환 전에 객체가 우항의 인스턴스가 맞는지 확인을 거치는 코드를 만들 수 있다. 타입 변환이 불가능한데 강제로 변환하다간 ClassCastException 오류가 발생한다.

if(parent instanceof child){ // 좌측의 child에는 Parent 와 Child의 인스턴스(parent, child) 모두 들어갈 수 있다.
  Child child = (child) parent;
}

추상 클래스 (abstract)

부모 클래스 자식 클래스와 비슷한 개념이지만 내가 확 체감되는 차이는
부모 자식 관계의 클래스에서는 부모 클래스를 new를 통해 인스턴스로 만들 수 있지만, 추상 클래스와 실체 클래스 관계에서는 추상 클래스가 public abstract class 클래스_이름 { ... } 형태로 구성되어있으며, 추상 클래스만을 따로 new를 통해 인스턴스로 만들 수 없다. 오직, 추상 클래스의 필드와 메소드는 추상 클래스를 상속 받은 실체 클래스를 통해서만 확인할 수 있다. 즉, extends 를 통해서만 호출이 가능하다.

추상 메소드와 재정의

추상 클래스에서 모든 실체들이 가지고 있는 메소드의 선언이 동일하지만 실행 내용은 각 실체마다 달라야 할 경우엔 추상 메소드를 선언하면 된다.
예를 들어 동물 이라는 추상 클래스에서 sound 라는 메소드를 선언하고 싶을때 모든 동물은 울음소리를 가지지만 각자 내는 소리가 다름으로 한 메소드로 선언하기는 어렵다. 이때 추상 메소드 sound를 선언한 후 메소드 내용물은 각 클래스에서 다시 설정하도록 하면 된다.

[public | protected] abstract 리턴타입 메소드이름(매개변수, ...);

이렇게 추상 메소드를 만들면 하위 클래스가 반드시 실행 내용을 채워야한다. 그렇지 않으면 컴파일 에러가 발생한다. 이게 추상 메소드의 강력한 이유이다.

sound 추상 메소드를 만든 후에는 실체 클래스는

// 추상 클래스
public abstract class Animal {
  public abstract void sound();
}
// 실체 클래스
public class Dog extends Animal {
  @Override
  public void sound(){
    System.out.println("멍멍"); // 비어있던 추상 메소드 재정의.
  }
}
profile
개강했기에 가끔씩 업로드.

0개의 댓글