웹사이트 검색

Java에서 불변 클래스를 만드는 방법


소개

이 기사에서는 Java 프로그래밍에서 불변 클래스를 작성하는 방법에 대한 개요를 제공합니다.

개체가 초기화된 후 상태가 변경되지 않으면 개체는 불변입니다. 예를 들어 String 클래스가 Java에서 불변인 이유입니다.

불변 객체는 업데이트할 수 없기 때문에 프로그램은 모든 상태 변경에 대해 새 객체를 생성해야 합니다. 그러나 불변 객체에는 다음과 같은 이점도 있습니다.

  • 불변 클래스는 값 변경에 대해 걱정할 필요가 없기 때문에 캐싱 목적에 적합합니다.
  • 불변 클래스는 본질적으로 스레드로부터 안전하므로 다중 스레드 환경에서 스레드 안전성에 대해 걱정할 필요가 없습니다.

Java 멀티스레딩 인터뷰 질문에 대해 자세히 알아보세요.

Java에서 불변 클래스 만들기

Java에서 불변 클래스를 만들려면 다음 일반 원칙을 따라야 합니다.

  1. 확장할 수 없도록 클래스를 최종으로 선언합니다.
  2. 직접 액세스가 허용되지 않도록 모든 필드를 비공개로 만드십시오.
  3. 변수에 대한 setter 메소드를 제공하지 마십시오.
  4. 필드의 값이 한 번만 할당될 수 있도록 모든 변경 가능한 필드를 최종으로 만듭니다.
  5. 딥 카피를 수행하는 생성자 메서드를 사용하여 모든 필드를 초기화합니다.
  6. 실제 개체 참조를 반환하는 대신 복사본을 반환하도록 getter 메서드에서 개체 복제를 수행합니다.

다음 클래스는 불변성의 기본을 설명하는 예제입니다. FinalClassExample 클래스는 필드를 정의하고 전체 복사를 사용하여 개체를 초기화하는 생성자 메서드를 제공합니다. FinalClassExample.java 파일의 main 메서드에 있는 코드는 개체의 불변성을 테스트합니다.

FinalClassExample.java라는 새 파일을 만들고 다음 코드를 복사합니다.

import java.util.HashMap;
import java.util.Iterator;

public final class FinalClassExample {

	// fields of the FinalClassExample class
	private final int id;
	
	private final String name;
	
	private final HashMap<String,String> testMap;

	
	public int getId() {
		return id;
	}

	public String getName() {
		return name;
	}

	// Getter function for mutable objects

	public HashMap<String, String> getTestMap() {
		return (HashMap<String, String>) testMap.clone();
	}

	// Constructor method performing deep copy
	
	public FinalClassExample(int i, String n, HashMap<String,String> hm){
		System.out.println("Performing Deep Copy for Object initialization");

		// "this" keyword refers to the current object
		this.id=i;
		this.name=n;

		HashMap<String,String> tempMap=new HashMap<String,String>();
		String key;
		Iterator<String> it = hm.keySet().iterator();
		while(it.hasNext()){
			key=it.next();
			tempMap.put(key, hm.get(key));
		}
		this.testMap=tempMap;
	}

	// Test the immutable class

	public static void main(String[] args) {
		HashMap<String, String> h1 = new HashMap<String,String>();
		h1.put("1", "first");
		h1.put("2", "second");
		
		String s = "original";
		
		int i=10;
		
		FinalClassExample ce = new FinalClassExample(i,s,h1);
		
		// print the ce values
		System.out.println("ce id: "+ce.getId());
		System.out.println("ce name: "+ce.getName());
		System.out.println("ce testMap: "+ce.getTestMap());
		// change the local variable values
		i=20;
		s="modified";
		h1.put("3", "third");
		// print the values again
		System.out.println("ce id after local variable change: "+ce.getId());
		System.out.println("ce name after local variable change: "+ce.getName());
		System.out.println("ce testMap after local variable change: "+ce.getTestMap());
		
		HashMap<String, String> hmTest = ce.getTestMap();
		hmTest.put("4", "new");
		
		System.out.println("ce testMap after changing variable from getter methods: "+ce.getTestMap());

	}

}

프로그램을 컴파일하고 실행합니다:

  1. javac FinalClassExample.java
  2. java FinalClassExample

참고: 파일을 컴파일할 때 다음 메시지가 표시될 수 있습니다. 참고: FinalClassExample.java는 확인되지 않은 또는 안전하지 않은 작업을 사용합니다. 를 객체로. 이 예제에서는 컴파일러 경고를 무시해도 됩니다.

다음과 같은 결과가 나타납니다.

Output
Performing Deep Copy for Object initialization ce id: 10 ce name: original ce testMap: {1=first, 2=second} ce id after local variable change: 10 ce name after local variable change: original ce testMap after local variable change: {1=first, 2=second} ce testMap after changing variable from getter methods: {1=first, 2=second}

출력은 생성자가 전체 복사를 사용하고 getter 함수가 원래 객체의 복제본을 반환하기 때문에 HashMap 값이 변경되지 않았음을 보여줍니다.

깊은 복사 및 복제를 사용하지 않으면 어떻게 됩니까?

FinalClassExample.java 파일을 변경하여 전체 복사 대신 얕은 복사를 사용하고 복사본의 insetad 개체를 반환할 때 어떤 일이 발생하는지 표시할 수 있습니다. 개체는 더 이상 변경할 수 없으며 변경할 수 있습니다. 예제 파일을 다음과 같이 변경합니다(또는 코드 예제에서 복사하여 붙여넣기).

  • 전체 복사를 제공하는 생성자 메서드를 삭제하고 다음 예에서 강조 표시된 얕은 복사를 제공하는 생성자 메서드를 추가합니다.
  • getter 함수에서 return (HashMap) testMap.clone();을 삭제하고 return testMap;을 추가합니다.

이제 예제 파일은 다음과 같아야 합니다.

import java.util.HashMap;
import java.util.Iterator;

public final class FinalClassExample {

	// fields of the FinalClassExample class
	private final int id;
	
	private final String name;
	
	private final HashMap<String,String> testMap;

	
	public int getId() {
		return id;
	}

	public String getName() {
		return name;
	}

	// Getter function for mutable objects

	public HashMap<String, String> getTestMap() {
		return testMap;
	}

	//Constructor method performing shallow copy

	public FinalClassExample(int i, String n, HashMap<String,String> hm){
		System.out.println("Performing Shallow Copy for Object initialization");
		this.id=i;
		this.name=n;
		this.testMap=hm;
	}

	// Test the immutable class

	public static void main(String[] args) {
		HashMap<String, String> h1 = new HashMap<String,String>();
		h1.put("1", "first");
		h1.put("2", "second");
		
		String s = "original";
		
		int i=10;
		
		FinalClassExample ce = new FinalClassExample(i,s,h1);
		
		// print the ce values
		System.out.println("ce id: "+ce.getId());
		System.out.println("ce name: "+ce.getName());
		System.out.println("ce testMap: "+ce.getTestMap());
		// change the local variable values
		i=20;
		s="modified";
		h1.put("3", "third");
		// print the values again
		System.out.println("ce id after local variable change: "+ce.getId());
		System.out.println("ce name after local variable change: "+ce.getName());
		System.out.println("ce testMap after local variable change: "+ce.getTestMap());
		
		HashMap<String, String> hmTest = ce.getTestMap();
		hmTest.put("4", "new");
		
		System.out.println("ce testMap after changing variable from getter methods: "+ce.getTestMap());

	}

}

프로그램을 컴파일하고 실행합니다:

  1. javac FinalClassExample.java
  2. java FinalClassExample

다음과 같은 결과가 나타납니다.

Output
Performing Shallow Copy for Object initialization ce id: 10 ce name: original ce testMap: {1=first, 2=second} ce id after local variable change: 10 ce name after local variable change: original ce testMap after local variable change: {1=first, 2=second, 3=third} ce testMap after changing variable from getter methods: {1=first, 2=second, 3=third, 4=new}

출력은 생성자 메서드가 getter 함수의 원래 개체에 대한 직접 참조가 있는 얕은 복사를 사용하기 때문에 HashMap 값이 변경되었음을 보여줍니다.

결론

딥 카피의 중요성을 포함하여 Java에서 불변 클래스를 생성할 때 따라야 할 몇 가지 일반 원칙을 배웠습니다. 더 많은 Java 자습서로 학습을 계속하십시오.