Java에서 불변 객체를 만드는 방법은 무엇입니까?
불변이라고 부르는 객체는 무엇입니까?
모든 정적 멤버가있는 클래스가 있으면 변경할 수 없습니까?
Java에서 불변 객체를 만드는 방법은 무엇입니까?
불변이라고 부르는 객체는 무엇입니까?
모든 정적 멤버가있는 클래스가 있으면 변경할 수 없습니까?
답변:
다음은 불변 객체 의 엄격한 요구 사항입니다.
final있지만 객체는 여전히 변경 가능합니다. 즉 private final Date imStillMutable). defensive copies이러한 경우에 만들어야 합니다.수업을 만드는 이유 final는 매우 미묘하고 종종 간과됩니다. 최종 사용자가 아닌 경우 자유롭게 클래스를 확장하고, 재정의 public또는 protected동작을 수행하고, 변경 가능한 속성을 추가하고, 해당 하위 클래스를 대체로 제공 할 수 있습니다. 클래스 final를 선언함으로써 이런 일이 발생하지 않도록 할 수 있습니다.
실제 문제를 보려면 아래 예를 고려하십시오.
public class MyApp{
/**
* @param args
*/
public static void main(String[] args){
System.out.println("Hello World!");
OhNoMutable mutable = new OhNoMutable(1, 2);
ImSoImmutable immutable = mutable;
/*
* Ahhhh Prints out 3 just like I always wanted
* and I can rely on this super immutable class
* never changing. So its thread safe and perfect
*/
System.out.println(immutable.add());
/* Some sneak programmer changes a mutable field on the subclass */
mutable.field3=4;
/*
* Ahhh let me just print my immutable
* reference again because I can trust it
* so much.
*
*/
System.out.println(immutable.add());
/* Why is this buggy piece of crap printing 7 and not 3
It couldn't have changed its IMMUTABLE!!!!
*/
}
}
/* This class adheres to all the principles of
* good immutable classes. All the members are private final
* the add() method doesn't modify any state. This class is
* just a thing of beauty. Its only missing one thing
* I didn't declare the class final. Let the chaos ensue
*/
public class ImSoImmutable{
private final int field1;
private final int field2;
public ImSoImmutable(int field1, int field2){
this.field1 = field1;
this.field2 = field2;
}
public int add(){
return field1+field2;
}
}
/*
This class is the problem. The problem is the
overridden method add(). Because it uses a mutable
member it means that I can't guarantee that all instances
of ImSoImmutable are actually immutable.
*/
public class OhNoMutable extends ImSoImmutable{
public int field3 = 0;
public OhNoMutable(int field1, int field2){
super(field1, field2);
}
public int add(){
return super.add()+field3;
}
}
실제로 Dependency Injection 환경에서 위의 문제가 발생하는 것은 매우 일반적입니다. 당신은 명시 적으로 인스턴스화하지 않으며 주어진 수퍼 클래스 참조는 실제로 하위 클래스 일 수 있습니다.
중요한 점은 불변성에 대한 확실한 보장을하려면 클래스를 final. 이것은 Joshua Bloch의 Effective Java 에서 자세히 다루고 Java 메모리 모델 사양에서 명시 적으로 참조 됩니다.
클래스에 공개 뮤 테이터 (세터) 메서드를 추가하지 마십시오.
private이거나이어야합니다 final. 상속을 피하기 위해. 상속은 캡슐화를 위반하기 때문입니다.
클래스는 불변이 아니며 객체는 불변입니다.
불변의 의미 : 내 공개 표시 상태는 초기화 후에 변경할 수 없습니다.
필드를 최종적으로 선언 할 필요는 없지만 스레드 안전성을 보장하는 데 큰 도움이 될 수 있습니다.
클래스에 정적 멤버 만있는 경우 해당 개체의 상태를 변경할 수 없기 때문에이 클래스의 개체는 변경할 수 없습니다 (아마도 만들 수 없습니다. :)).
Java에서 클래스를 변경 불가능하게 만들려면 다음 사항에 유의하십시오.
1. 클래스의 인스턴스 변수 값을 수정하는 setter 메소드를 제공하지 마십시오.
2. 클래스를 'final' 로 선언합니다 . 이것은 다른 클래스가 그것을 확장하는 것을 막아서 인스턴스 변수 값을 수정할 수있는 메소드를 재정의하는 것을 방지합니다.
3. 인스턴스 변수를 private 및 final 로 선언합니다 .
4. 클래스의 생성자를 전용 으로 선언하고 필요할 때 클래스의 인스턴스를 생성하는 팩토리 메서드를 추가 할 수도 있습니다 .
이 점이 도움이 될 것입니다 !!
private합니까?
에서 오라클 사이트, 어떻게 자바에서 불변의 객체를 만들 수 있습니다.
- 필드가 참조하는 필드 또는 개체를 수정하는 "세터"메서드를 제공하지 마십시오.
- 모든 필드를 최종 및 비공개로 설정합니다.
- 하위 클래스가 메서드를 재정의하도록 허용하지 마십시오. 이를 수행하는 가장 간단한 방법은 클래스를 final로 선언하는 것입니다. 보다 정교한 접근 방식은 생성자를 비공개로 만들고 팩토리 메서드에서 인스턴스를 생성하는 것입니다.
- 인스턴스 필드에 변경 가능한 개체에 대한 참조가 포함 된 경우 해당 개체의 변경을 허용
하지 마십시오 . I. 변경 가능한 개체를 수정하는 메서드를 제공하지 마십시오.
II. 변경 가능한 객체에 대한 참조를 공유하지 마십시오. 생성자에 전달 된 변경 가능한 외부 객체에 대한 참조를 저장하지 마십시오. 필요한 경우 복사본을 만들고 복사본에 대한 참조를 저장합니다. 마찬가지로 메서드에서 원본을 반환하지 않도록 필요한 경우 내부 변경 가능 개체의 복사본을 만듭니다.
불변 객체는 생성 후 내부 상태를 변경하지 않는 객체입니다. 동기화없이 스레드간에 공유 할 수 있기 때문에 다중 스레드 응용 프로그램에서 매우 유용 합니다.
1. setter 메서드를 추가하지 마십시오.
불변 객체를 빌드하는 경우 내부 상태는 변경되지 않습니다. setter 메서드의 작업은 필드의 내부 값을 변경하여 추가 할 수 없도록하는 것입니다.
2. 모든 필드를 최종 및 비공개로 선언
비공개 필드는 클래스 외부에서 볼 수 없으므로 수동 변경 사항을 적용 할 수 없습니다.
최종 필드를 선언하면 기본 값을 참조하는 경우 개체를 참조하는 경우 값이 변경되지 않고 참조를 변경할 수 없습니다. 이것은 private final 필드 만있는 객체가 변경되지 않도록하기에는 충분하지 않습니다.
3. 필드가 변경 가능한 객체 인 경우 getter 메서드에 대한 방어 복사본을 만듭니다.
우리는 내부 상태를 변경할 수 있기 때문에 필드 final 및 private을 정의하는 것으로 충분하지 않다는 것을 전에 보았습니다. 이 문제를 해결하려면 해당 필드의 방어적인 복사본을 만들고 요청 될 때마다 해당 필드를 반환해야합니다.
4. 생성자에 전달 된 변경 가능한 개체가 필드에 할당되어야하는 경우 방어적인 복사본을 만듭니다.
변경이 가능하기 때문에 생성자에 전달 된 참조를 보유하는 경우에도 동일한 문제가 발생합니다. 따라서 생성자에 전달 된 객체에 대한 참조를 보유하면 변경 가능한 객체를 만들 수 있습니다. 이 문제를 해결하려면 매개 변수가 변경 가능한 객체 인 경우 방어적인 매개 변수 사본을 만들어야합니다.
참고 필드는 불변 오브젝트에 대한 참조인지 생성자하고 최종적으로 전용 필드를 정의하기에 충분 게터 방법에 운전자의 복사본을 생성 할 필요가 없다.
5. 하위 클래스가 메서드를 재정의하도록 허용하지 마십시오.
하위 클래스가 메서드를 재정의하면 방어적인 복사본 대신 변경 가능한 필드의 원래 값을 반환 할 수 있습니다.
이러한 간단한 규칙을 따르면 스레드로부터 안전하므로 스레드간에 변경 불가능한 객체를 자유롭게 공유 할 수 있습니다!
옳고 그름은 없으며 당신이 선호하는 것에 달려 있습니다. 그것은 당신의 선호도와 당신이 성취하고자하는 것에 달려 있습니다 (그리고 어느 한쪽의 열렬한 팬들을 소외시키지 않고 두 접근법을 쉽게 사용할 수 있다는 것은 일부 언어가 추구하는 성배입니다).
우선 불변 객체를 만들어야하는 이유와 불변 객체의 장점은 무엇인지 알고 있습니다.
불변 객체의 장점
동시성 및 멀티 스레딩 자동으로 스레드로부터 안전하므로 동기화 문제가 발생합니다 .... etc
생성자 를 복사 할 필요가 없습니다 . 복제 를 구현할 필요가 없습니다 . 클래스를 재정의 할 수 없음 필드를 비공개 및 최종 강제 호출자가 인수없는 생성자를 사용하는 대신 단일 단계에서 개체를 완전히 생성하도록합니다.
불변 객체는 단순히 상태가 불변 객체가 생성 된 후에 객체의 데이터를 변경할 수 없음을 의미하는 객체입니다.
아래 코드를 참조하십시오.
public final class ImmutableReminder{
private final Date remindingDate;
public ImmutableReminder (Date remindingDate) {
if(remindingDate.getTime() < System.currentTimeMillis()){
throw new IllegalArgumentException("Can not set reminder" +
" for past time: " + remindingDate);
}
this.remindingDate = new Date(remindingDate.getTime());
}
public Date getRemindingDate() {
return (Date) remindingDate.clone();
}
}
가변성 최소화
불변 클래스는 단순히 인스턴스를 수정할 수없는 클래스입니다. 각 인스턴스에 포함 된 모든 정보는 생성시 제공되며 개체의 수명 동안 고정됩니다.
JDK 불변 클래스 : 문자열, 박스형 기본 클래스 (래퍼 클래스), BigInteger 및 BigDecimal 등
클래스를 변경 불가능하게 만드는 방법은 무엇입니까?
방어적인 사본을 만드십시오. 변경 가능한 구성 요소에 대한 독점적 인 액세스를 보장합니다.
public List getList () {return Collections.unmodifiableList (list); <=== 호출자에게 반환하기 전에 변경 가능한 필드의 방어 복사본}
클래스에 변경 가능한 객체를 참조하는 필드가있는 경우 클래스의 클라이언트가 이러한 객체에 대한 참조를 얻을 수 없는지 확인하십시오. 이러한 필드를 클라이언트 제공 개체 참조로 초기화하거나 접근 자로부터 개체 참조를 반환하지 마십시오.
import java.util.Date;
public final class ImmutableClass {
public ImmutableClass(int id, String name, Date doj) {
this.id = id;
this.name = name;
this.doj = doj;
}
private final int id;
private final String name;
private final Date doj;
public int getId() {
return id;
}
public String getName() {
return name;
}
/**
* Date class is mutable so we need a little care here.
* We should not return the reference of original instance variable.
* Instead a new Date object, with content copied to it, should be returned.
* */
public Date getDoj() {
return new Date(doj.getTime()); // For mutable fields
}
}
import java.util.Date;
public class TestImmutable {
public static void main(String[] args) {
String name = "raj";
int id = 1;
Date doj = new Date();
ImmutableClass class1 = new ImmutableClass(id, name, doj);
ImmutableClass class2 = new ImmutableClass(id, name, doj);
// every time will get a new reference for same object. Modification in reference will not affect the immutability because it is temporary reference.
Date date = class1.getDoj();
date.setTime(date.getTime()+122435);
System.out.println(class1.getDoj()==class2.getDoj());
}
}
자세한 내용은 내 블로그를 참조하십시오.
http://javaexplorer03.blogspot.in/2015/07/minimize-mutability.html
객체가 생성 된 후 상태를 변경할 수없는 경우 불변이라고합니다. Java에서 변경 불가능한 클래스를 생성하는 가장 간단한 방법 중 하나는 모든 필드를 final로 설정하는 것입니다. "java.util.Date"와 같은 변경 가능한 클래스를 포함하는 변경 불가능한 클래스를 작성해야하는 경우. 이러한 경우 불변성을 보존하기 위해 원본 객체의 사본을 반환하는 것이 좋습니다.
불변 객체는 일단 생성되면 상태를 변경할 수없는 객체입니다. 예를 들어 String 클래스는 불변 클래스입니다. 변경 불가능한 개체는 수정할 수 없으므로 동시 실행에서도 스레드로부터 안전합니다.
불변 클래스의 특징 :
불변 클래스를 작성하기위한 키 :
클래스를 불변 클래스로 원할 때 다음 몇 단계를 고려해야합니다.
위에서 입력 한 내용을 살펴 보겠습니다.
//ImmutableClass
package younus.attari;
public final class ImmutableExample {
private final String name;
private final String address;
public ImmutableExample(String name,String address){
this.name=name;
this.address=address;
}
public String getName() {
return name;
}
public String getAddress() {
return address;
}
}
//MainClass from where an ImmutableClass will be called
package younus.attari;
public class MainClass {
public static void main(String[] args) {
ImmutableExample example=new ImmutableExample("Muhammed", "Hyderabad");
System.out.println(example.getName());
}
}
@ nsfyn55에서 제공하는 답변까지 추가, 다음과 같은 측면은 또한 가능한 객체 불변성, 고려 될 필요가 주요 중요성
다음 클래스를 고려하십시오.
public final class ImmutableClass {
private final MutableClass mc;
public ImmutableClass(MutableClass mc) {
this.mc = mc;
}
public MutableClass getMutClass() {
return this.mc;
}
}
public class MutableClass {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
public class MutabilityCheck {
public static void main(String[] args) {
MutableClass mc = new MutableClass();
mc.setName("Foo");
ImmutableClass iMC = new ImmutableClass(mc);
System.out.println(iMC.getMutClass().getName());
mc.setName("Bar");
System.out.println(iMC.getMutClass().getName());
}
}
다음은 MutabilityCheck의 출력입니다.
Foo
Bar
다음 사항에 유의하는 것이 중요합니다.
다음 변경 사항에 의해 설명 된 불변의 인스턴스 변수 에 '복사' 또는 '클로 잉' 하여 불변 객체 (constructor를 통해)에 변경 가능한 객체를 생성 합니다.
public final class ImmutableClass {
private final MutableClass mc;
public ImmutableClass(MutableClass mc) {
this.mc = new MutableClass(mc);
}
public MutableClass getMutClass() {
return this.mc;
}
}
public class MutableClass {
private String name;
public MutableClass() {
}
//copy constructor
public MutableClass(MutableClass mc) {
this.name = mc.getName();
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
다음이 MutabilityCheck 클래스에서 여전히 유효하기 때문에 여전히 완전한 불변성을 보장하지 않습니다.
iMC.getMutClass().setName("Blaa");
그러나 1에서 변경 한 내용으로 MutabilityCheck를 실행하면 다음과 같은 결과가 출력됩니다.
Foo
Foo
객체에 대한 완전한 불변성을 달성하려면 모든 종속 객체도 불변이어야합니다.
JEP 359 가있는 JDK 14+부터 " records"를 사용할 수 있습니다 . Immutable 클래스를 만드는 가장 간단하고 번거로운 방법입니다.
레코드 클래스는 레코드 에 대한 설명을 제공하는 레코드로 알려진 고정 필드 세트에 대한 얕은 불변 의 투명한 캐리어입니다 . 각각 은 제공된 값을 보유 하는 필드와 값 을 검색 하는 방법을 제공 합니다. 필드 이름과 접근 자 이름은 구성 요소의 이름과 일치합니다.componentsstatecomponentfinalaccessor
불변의 직사각형을 만드는 예를 고려해 보겠습니다.
record Rectangle(double length, double width) {}
생성자를 선언 할 필요가 없으며 equals& hashCode메소드 를 구현할 필요도 없습니다 . 모든 기록에는 이름과 상태 설명이 필요합니다.
var rectangle = new Rectangle(7.1, 8.9);
System.out.print(rectangle.length()); // prints 7.1
객체 생성 중에 값의 유효성을 검사하려면 생성자를 명시 적으로 선언해야합니다.
public Rectangle {
if (length <= 0.0) {
throw new IllegalArgumentException();
}
}
레코드의 본문은 정적 메서드, 정적 필드, 정적 이니셜 라이저, 생성자, 인스턴스 메서드 및 중첩 유형을 선언 할 수 있습니다.
인스턴스 방법
record Rectangle(double length, double width) {
public double area() {
return this.length * this.width;
}
}
정적 필드, 메서드
상태는 구성 요소의 일부 여야하므로 레코드에 인스턴스 필드를 추가 할 수 없습니다. 그러나 정적 필드와 메서드를 추가 할 수 있습니다.
record Rectangle(double length, double width) {
static double aStaticField;
static void aStaticMethod() {
System.out.println("Hello Static");
}
}