[Java] 참조 변수, 메서드와 생성자
배열 초기화 시 배열 크기 반드시 설정해줘야 한다
int [] myArray = new int []; // error "array dimension missing"
반드시 배열 초기화 시 배열 크기를 설정해줘야 한다.
그래야 배열을 초기화 한 것이다.
int [] myArray = new int [100]; // good
참조 변수 헷갈릴만한 예제
반드시 primitive type이 아닌 변수는 모두 참조 변수라서 값을 저장하거나, 실행하거나, 호출하려면 반드시 new 연산자를 통해서 객체를 생성해줘야 한다는 것을 주지하자.
아래의 코드는 틀린 코드이다. 틀린 부분을 찾아보자.
MyRectangle [] rects = new MyRectangle [100];
rects[n].lu = new MyPoint();
바로 틀린 부분은 rects[n] 이라는 rects 배열의 원소 하나 또한 MyRectangle이란 클래스 타입을 가진 참조변수인데, 객체 생성이 빠졌다.
아래의 코드는 맞는 코드이다.
MyRectangle [] rects = new MyRectangle [100];
rects[n] = new MyRectangle(); // 추가
rects[n].lu = new MyPoint();
메서드와 생성자
메서드와 생성자를 통해 클래스의 응집도를 높이고 결합도를 낮춘다
클래스는 관련 있는 Data 와 method들을 하나의 단위로 묶은 것이다.
즉, 객체 지향 프로그래밍의 사상대로 "관련 있는 것을 하나로 묶은 것"이다.
이를 통해 코드의 응집도를 높이고 결합도를 낮출 수 있다.
생성자
1. return 타입 자체가 존재하지 않는다.
메소드의 경우 return 값이 없으면 void라는 리턴 타입이 있다.
그러나 생성자를 return 타입 자체가 없다.
2. 클래스와 이름이 같음
생성자의 역할
주목적은 객체의 데이터 필드 값을 초기화하는 것
생성자는 new 명령어로 객체가 생성될 때 자동으로 실행됨
=> 객체의 생성 + 초기화 한 번에 가능!
생성자의 장점
데이터 필드의 값 초기화 라는 생성자의 역할과 연관 됨
생성자를 통해 클래스 안에 필드 초기화 코드를 모두 관리 할 수 있음
따라서 로직 변경 시 생성자가 없다면 초기화해주는 많은 클래스의 코드들을 모두 손봐야 하지만,
생성자가 있으면 초기화해주는 부분은 클래스 하나의 생성자 속 초기화 부분 + 해당 클래스 내부 로직만 수정하면 되므로 "코드의 수정을 최소화" 할 수 있음!
즉, 클래스 외부의 코드는 수정 안해도 됨!
따라서
코드의 응집도는 높이고 결합도, 상호의존성는 낮출 수 있음
cf. 상호 의존성
하나의 클래스에서 수정하게 되는 일이 줄어들음
하나의 클래스 안에 관련 있는 거 다 모여서 결합도 낮추고 상호의존성 낮아진다.
생성자가 자동 생성된다?
1. 클래스에 아무 생성자가 없으면 자동으로 no parameter 생성자를 형성해줌
2. 자식 클래스 생성자 내부에서 명시적으로 부모 생성자를 호출하지 않는 경우, 무조건 자동으로 super();를 추가해줌
(이때 만약에 부모클래스의 기본 생성자가 no parameter 생성자가 아닌 경우에
Implicit super constructor Parent() is undefined. Must explicitly invoke another constructor
같은 에러가 발생하는 것)
주의할 점은 생성자에서 타 생성자를 호출할 때는 반드시 첫 줄에서 하나만 호출이 가능하다는 것 !
(중복 초기화를 막고 딱 한 번만 생성자를 호출하기 위해서 그러함)
package Section1;
public class Child extends Parent{
private String name;
private int number;
public Child(int number, String name) { // 먼저 호출된다고 가정
this.name = name;
this.number = number;
this("홍길동"); // 생성자가 첫 줄에서 호출이 안되어서 에러
System.out.println("Child() call");
}
public Child(String name) {
this("홍길동", "안녕");
super(); // 생성자가 두 번 호출되어서 에러
this.name = name;
System.out.println("Child(String name) call");
}
}
슈퍼 클래스에 매개변수가 있는 생성자만 있고 자식 클래스에 아무 생성자도 정의되어 있지 않을 때
// 부모 클래스
public class Animal {
public Animal(int age, String kind) { // 부모 클래스에 매개변수가 있는 생성자가 존재
this.age = age;
this.kind = kind;
}
}
// 자식 클래스
public class Cat extends Animal {
// 생성자가 없으므로 기본 생성자가 만들어지고 super();가 그 내부에서 호출된다.
// 에러 발생
// Implicit super constructor Animal() is undefined for default constructor.
// Must define an explicit constructor
}
이 경우에도 에러가 발생한다.
그 이유는 자식 클래스에 아무 생성자가 없을 시, 그리고 명시적으로 부모 클래스를 호출하지 않는 경우 자동적으로 super(); 만을 추가해주는데, 슈퍼 클래스의 기본 생성자는 매개변수가 있는 생성자이므로 자식 클래스의 객체를 생성했을 때 부모 클래스의 객체 생성 과정에서 생성자를 통해 반드시 필요한 초기화 과정이 일어나지 않는다.
따라서 자식 클래스에선 생성자를 만들고 거기서 명시적으로 부모 클래스의 매개변수가 있는 생성자를 호출해주어야 한다.
// 부모 클래스
public class Animal {
public Animal(int age, String kind) { // 부모 클래스에 매개변수가 있는 생성자가 존재
this.age = age;
this.kind = kind;
}
}
// 자식 클래스
public class Cat extends Animal {
public Cat(int age, String kind) {
super(age, kind); // 명시적으로 부모 클래스의 매개변수 있는 생성자 호출
}
}
슈퍼 클래스에 매개변수가 있는 생성자만 있고 자식 클래스에 super() 생성자가 있을 때
// 부모 클래스
class Employee {
private String name;
private double salary;
private MyDate birthDate;
public Employee(String name, double salary, MyDate birthDate) {
super();
this.name = name;
this.salary = salary;
this.birthDate = birthDate;
}
}
// 자식 클래스
public class Manager extends Employee {
private String dept;
public Manager(String dept) {
super();
this.dept = dept;
}
}
- 위 코드에서는 에러가 난다. 자식 객체를 생성할 때 생성자 호출시 문제가 생기기 때문이다.
- 자식이 만들어지려면 메모리상에 부모가 먼저 올라가야 한다. 이 말은 자식 객체가 생성되려면 무조건 부모 객체가 생성되어야 한다는 말이고, 부모 생성자 호출이 일어나야 한다는 말이다.
- 위의 부모 클래스에서 기본 생성자는 no parameter constructor가 아니라, parameter가 있는 생성자이다.
따라서 자식 객체 생성 시 반드시 메모리에 부모 객체가 올라가야 하는데, 기본 생성자가 호출되어야 하므로 자식 클래스의 생성자에서 매개변수가 있는 부모 클래스의 parameter가 있는 생성자를 반드시 호출해줘야 하는 것이다.
- 그런데 부모 생성자의 매개변수(name, salary, birthDate)가 상속받은 자식 생성자에게 전달되지 않고 있다!
해결 방법은 2가지가 있다.
1. 부모클래스에 전달하는 인자값이 없는 기본 생성자 만들어주기
// 부모 클래스
class Employee {
private String name;
private double salary;
private MyDate birthDate;
public Employee(){} // 부모의 기본 생성자 추가!!!!!!!
public Employee(String name, double salary, MyDate birthDate) {
super();
this.name = name;
this.salary = salary;
this.birthDate = birthDate;
}
}
// 자식 클래스
public class Manager extends Employee {
private String dept;
public Manager(String dept) {
super();
this.dept = dept;
}
}
2. 자식클래스의 생성자 안에 부모 클래스의 매개변수 생성자인 super(매개변수);를 선언해주기
// 부모 클래스
class Employee {
private String name;
private double salary;
private MyDate birthDate;
public Employee(String name, double salary, MyDate birthDate) {
super();
this.name = name;
this.salary = salary;
this.birthDate = birthDate;
}
}
// 자식 클래스
public class Manager extends Employee {
private String dept;
public Manager(String name, double salary, MyDate birthDate, String dept) {
// 자식 클래스의 생성자에 부모 필드를 함께 매개변수로 넣고
super(name, salary, birthDate); // 부모 클래스 생성자 선언
this.dept = dept;
}
}