2.3.3 다른 생성자에서 특정 생성자 호출

  • 또 다른 생성자에서 어느 한 생성자를 호출할 수 있는데, 호출하는 쪽 생성자 바디의 첫 번째 문장으로만 허용한다.
  • 그리고 호출할 생성자 이름이 아니라 this 키워드를 사용한다.
public Employee(double salary){
	
    this("", salary); //Employee(String, double) 호출
    
    //이후에 다른 문장이 올 수 있다.
}

 

2.3.4 기본 초기화

  • 생성자 안에서 인스턴스 변수를 명시적으로 설정하지 않으면 자동으로 변수를 기본 값으로 설정한다. 숫자는 0, bool 값은 false, 객체 참조는 null이 기본 값

 

2.3.5 인스턴스 변수 초기화

  • 객체를 할당하고 나서 생성자가 실행되기 전에 일어난다.
  • 인스턴스 변수를 선언할 때 초기화하는 방법 외에 클래스 안에 임의의 초기화 블록을 넣는 방법도 있다.
  • 인스턴스 변수 초기화와 초기화 블록 클래스 선언에 나타난 순서로 실행하며, 그 다음에 생성자를 실행한다.
public class Employee(){
	
    private String name = " ";
    private int id;
    private double salary;
    
    { //초기화 블록
    	Random generator = new Random();
        id = 1 + generator.nextInt(1_000_000);
    }
    
    ...
}

 

2.4 정적 변수와 정적 메소드

 

2.4.3 정적 초기화 블록

  • 정적 변수에 초기화 작업이 추가로 필요할 때 사용한다.
  • 정적 초기화는 클래스를 처음 로드할 때 일어난다. 인스턴스 변수와 마찬가지로 정적 변수를 명시적으로 다른 값으로 설정하지 않으면 0이나 false, null이 된다.
  • 모든 정젹 변수는 초기화와 정적 초기화 블록은 클래스 선언 안에 나타난 순서로 실행된다.
public class CreditCardForm{
	
    private static final ArrayList<Integer> expiration = new ArrayList<>();
    static{
    	int year = LocalDate.now().getYear();

		for (int i = year; i <= year + 20; i++){
        	expirationYear.add(i);	
        }
    }
    
    ...
}

2.4.4 팩토리 메소드

  • 정적 메소드는 흔히 팩토리 메소드를 만드는데 사용한다.
  • 팩토리 메소드는 클래스의 새 인스턴스를 반환하는 정적 메소드를 의미한다.
NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance();

double x = 0.1;

System.out.println(currencyFormatter.format(x)); 

-> 왜 생성자 대신 팩토리 메소드를 사용할까? 

  • 생성자를 구별하는 유일한 방법은 생성자의 매개변수. 타입이다. 따라서 매개변수가 없는 생성자를 두개씩 둘 수 없다.
  • 게다가 new NumberFormat(…) 생성자는 NumberFormat을 돌려준다. 하지만 팩토리 메소드는 서브클래스의 객체를 반환할 수 있다.
  • 그리고 팩토리 메소드를 사용하면 불필요하게 새 객체를 생성하는 대신 공유 객체를 반환할 수 있다. 예를 들어 Collections.emptyList()를 호출하면 변경할 수 없는 빈 리스트(공유객체)를 반환한다.

 

2.6 중첩 클래스

  • 클래스를 다른 클래스 내부에 두는 방법이 있다. 이런 클래스를 중첩 클래스(nested class)라고 한다. 중첩 클래스는 가시성을 제한하거나 Element, Node, Item 처럼 일반적인 이름을 사용하면서도 정돈된 상태를 유지할 때 유용하다.
  • 자바에는 작동 방식이 약간 다른 중첩 클래스가 두 종류 있다.

 

2.6.1 정적 중첩 클래스

public class Invoice{

	private static class Item { //Invoice 내부에 Item 중첩했다.
		String description;
		int quantity;
		double unitPrice;

		double price() {return quantity * unitPrice; }
	}

	private ArrayList<Item> items = new ArrayList<>();
    
	public void addItem(String description, int quantity, double unitPrice) {
		
		Item newItem = new Item();
		
		newItem.description = description;
		newItem.quantity = quantity;
		newItem.unitPrice = unitPrice;
		
		items.add(newItem);
	}
}

Invoice.Item Item = new Invoice.Item();
myInvoice.add(newItem);

 

Invoice.Item 클래스와 다른 클래스 외부에 선언한 InvoiceItem 클래스는 근본적으로 차이가 없다. 클래스 중첩은 그저 Item 클래스가 청구서에 들어 있는 물품을 표현한다는 사실을 분명하게 할 뿐이다.

 

2.6.2 내부 클래스

  • static 을 붙이지 않은 클래스를 내부 클래스(이너 클래스 inner class)라고 한다.

각 회원이 다른 회원과 관계를 맺는 소셜 네트워크를 생각해 보자.

 

public class Network {

	public class Member{  //static 제어자를 빼면 근본적인 차이가 하나 생긴다. 예를 들어 Member 객체는 자신이 어느 네트워크에 속하는지 알게 된다.
		private String name;
		private ArrayList<Member> friends;

		public Member(String name) {
			this.name = name;
			friends = new ArrayList();
		}

		public void deactivate() {
			members.remove(this);
		}

		public boolean belongsTo(Network n){
			return Network.this == n;
		}
	}

	private ArrayList<Member> members = new ArrayList()<>;

	public Member enroll(String name) {
		Member newMember = new Member(name);
		members.add(newMember);
		return newMember;
	}
}

 

다음과 같이 멤버를 추가하고 참조를 얻을 수 있다.

Network myFace = new Network();
Network.Member fred = myFace.enroll("Fred");

이제 Fred는 멤버십을 해지하려고 한다.

fred.deactivate();

 

메소드 구현에서 볼 수 있듯이, 내부 클래스의 메소드는 외부 클래스의 인스턴스 변수에 접근할 수 있다.

이 코드에서는 내부 클래스를 생성한 외부 클래스 객체(myFace 네트워크)의 인스턴스 변수 members이다.

 

바로 이 점이 내부 클래스를 정적 중첩 클래스와 구별시키는 요인이다.

내부 클래스의 각 객체는 외부 클래스의 객체에 대한 참조를 포함한다.

 

members.remove(this); // 이 호출은 실제로 다음을 의미한다.
outer.members.remove(this);

 

여기서는 외부 클래스의 숨은 참조를 outer로 나타냈다.

정적 중첩 클래스에는 이런 참조가 없다.(static 메소드에 this 참조가 없는 것과 마찬가지)

 

중첩 클래스의 인스턴스가 자신을 감싸고 있는 클래스의 어느 인스턴스에 속하는지 알 필요가 없을 때 정적 중첩 클래스를 사용하자.

내부 클래스는 이 정보가 중요할 때만 사용하자.

 

2.6.3 내부 클래스용 특수 문법 규칙

 

앞에서는 내부 클래스 객체의 외부 클래스 참조를 outer로 지칭해서 설명했다. 외부 클래스 참조를 나타내는 실제 문법은 조금 더 복잡하다. 다음 표현식은 외부 클래스 참조를 나타낸다.

 

OuterClass.this

 

예를 들어 내부 클래스 Member의 deactivate 메소드는 다음과 같이 작성할 수 있다.

public void deactivate(){ 
	Network.this.members.remove(this)
}

여기서는 Network.this 문법은 필수가 아니다. 그냥 members로만 참조해도 암묵적으로 외부 클래스 참조를 사용한다.

하지만 외부 클래스 참조가 명시적으로 필요할 때도 있다.

public boolean belongsTo(Network n){
	return Network.this == n;
}

 

* 내부 클래스에는 시간 상수 외에 정적 멤버를 선언할 수 없다.

 

 

'Java' 카테고리의 다른 글

인터페이스와 람다 표현식 (1)  (0) 2020.09.09
Mac에 Java 설치하기  (1) 2017.03.05

+ Recent posts