문제

객체와 같은 구조체를 생성하는 것은 Java 방식에 완전히 위배됩니까?

class SomeData1 {
    public int x;
    public int y;
}

접근자와 변경자가 있는 클래스가 Java와 더 비슷하다는 것을 알 수 있습니다.

class SomeData2 {
    int getX();
    void setX(int x);

    int getY();
    void setY(int y);

    private int x;
    private int y;
}

첫 번째 예제의 클래스는 표기법상 편리합니다.

// a function in a class
public int f(SomeData1 d) {
    return (3 * d.x) / d.y;
}

이것은 그다지 편리하지 않습니다.

// a function in a class
public int f(SomeData2 d) {
    return (3 * d.getX()) / d.getY();
}
도움이 되었습니까?

해결책

이것은 일반적으로 논의되는 주제입니다.객체에 공개 필드를 생성할 때의 단점은 여기에 설정된 값을 제어할 수 없다는 것입니다.동일한 코드를 사용하는 프로그래머가 많은 그룹 프로젝트에서는 부작용을 피하는 것이 중요합니다.게다가 때로는 필드 객체의 복사본을 반환하거나 어떻게든 변환하는 것이 더 나을 때도 있습니다.테스트에서 이러한 메서드를 모의할 수 있습니다.새 클래스를 생성하면 가능한 모든 작업이 표시되지 않을 수 있습니다.이는 방어적인 프로그래밍과 같습니다. 언젠가 getter와 setter가 도움이 될 수 있으며 이를 생성/사용하는 데 많은 비용이 들지 않습니다.그래서 때로는 유용합니다.

실제로 대부분의 필드에는 간단한 getter 및 setter가 있습니다.가능한 해결책은 다음과 같습니다:

public property String foo;   
a->Foo = b->Foo;

업데이트:Java 7 또는 아마도 앞으로도 속성 지원이 추가될 가능성은 거의 없습니다.Groovy, Scala 등과 같은 다른 JVM 언어는 이제 이 기능을 지원합니다.- 알렉스 밀러

다른 팁

많은 Java 사람들은 Sun Java 코딩 가이드 라인에 익숙하지 않은 것으로 보입니다.이 가이드 라인은 클래스가 본질적으로 "구조"인 경우 공개 인스턴스 변수를 사용하는 것이 매우 적절하다고 말합니다.

사람들은 마치 마치 마치 자바의 중심에있는 것처럼 Getters와 Setter가 자바 방식이라고 생각하는 경향이 있습니다.그렇지 않습니다.적절한 상황에서 공개 인스턴스 변수를 사용하여 Sun Java 코딩 가이드 라인을 따르는 경우 실제로 불필요한 getters와 setters로 혼란스러운 코드를 작성하는 것보다 실제로 더 나은 코드를 작성하는 것입니다.

1999년의 Java 코드 규칙 그리고 여전히 변함이 없습니다.

10.1 인스턴스 및 클래스 변수에 대한 액세스 제공

정당한 이유 없이 인스턴스나 클래스 변수를 공개로 설정하지 마세요.인스턴스 변수는 명시적으로 설정하거나 가져올 필요가 없는 경우가 많습니다. 이는 메서드 호출의 부작용으로 발생하는 경우가 많습니다.

적절한 공용 인스턴스 변수의 한 가지 예는 클래스가 기본적으로 동작이 없는 데이터 구조인 경우입니다. 즉, 클래스 대신 구조체를 사용했다면(Java가 구조체를 지원하는 경우) 클래스의 인스턴스 변수를 공개로 만드는 것이 적절합니다..

http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-137265.html#177

http://en.wikipedia.org/wiki/Plain_old_data_structure

http://docs.oracle.com/javase/1.3/docs/guide/collections/designfaq.html#28

정말 상식을 사용하세요.다음과 같은 것이 있는 경우:

public class ScreenCoord2D{
    public int x;
    public int y;
}

그렇다면 getter와 setter로 포장하는 것은 거의 의미가 없습니다.다른 방법으로는 x, y 좌표를 전체 픽셀로 저장하지 않을 것입니다.게터와 세터는 속도를 늦출 뿐입니다.

반면에 다음과 같습니다.

public class BankAccount{
    public int balance;
}

나중에 잔액이 계산되는 방식을 변경하고 싶을 수도 있습니다.이것은 실제로 getter와 setter를 사용해야 합니다.

항상 아는 것이 더 좋습니다. 모범 사례를 적용하면 언제 규칙을 바꿔도 괜찮은지 알 수 있습니다.

변경 가능성 문제를 해결하기 위해 x 및 y를 최종으로 선언할 수 있습니다.예를 들어:

class Data {
  public final int x;
  public final int y;
  public Data( int x, int y){
    this.x = x;
    this.y = y;
  }
}

이러한 필드에 쓰려고 시도하는 코드를 호출하면 "필드 x가 최종으로 선언되었습니다."라는 컴파일 시간 오류가 발생합니다.할당할 수 없습니다."

그러면 클라이언트 코드는 귀하의 게시물에서 설명한 '간단한' 편의성을 가질 수 있습니다.

public class DataTest {
    public DataTest() {
        Data data1 = new Data(1, 5);
        Data data2 = new Data(2, 4);
        System.out.println(f(data1));
        System.out.println(f(data2));
    }

    public int f(Data d) {
        return (3 * d.x) / d.y;
    }

    public static void main(String[] args) {
        DataTest dataTest = new DataTest();
    }
}

사용하지 마세요 public 필드

사용하지 마세요 public 클래스의 내부 동작을 실제로 래핑하고 싶을 때 필드를 사용하세요.가져가다 java.io.BufferedReader 예를 들어.여기에는 다음과 같은 필드가 있습니다.

private boolean skipLF = false; // If the next character is a line feed, skip it

skipLF 모든 읽기 방법으로 읽고 씁니다.별도의 스레드에서 실행되는 외부 클래스가 악의적으로 상태를 수정한 경우에는 어떻게 되나요? skipLF 읽는 도중에? BufferedReader 확실히 엉망이 될 것입니다.

사용하세요 public 필드

이것을 받아라 Point 예를 들어 클래스:

class Point {
    private double x;
    private double y;

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public double getX() {
        return this.x;
    }

    public double getY() {
        return this.y;
    }

    public void setX(double x) {
        this.x = x;
    }

    public void setY(double y) {
        this.y = y;
    }
}

이렇게 하면 두 점 사이의 거리를 계산하는 것이 작성하기가 매우 어려워집니다.

Point a = new Point(5.0, 4.0);
Point b = new Point(4.0, 9.0);
double distance = Math.sqrt(Math.pow(b.getX() - a.getX(), 2) + Math.pow(b.getY() - a.getY(), 2));

클래스에는 일반 getter 및 setter 이외의 동작이 없습니다.클래스가 단지 데이터 구조를 나타내고 다음을 갖지 않는 경우 공개 필드를 사용하는 것이 허용됩니다. 그리고 결코 행동하지 않을 것입니다(씬 게터와 세터는 ~ 아니다 여기서는 행동으로 간주됩니다).다음과 같이 더 잘 작성할 수 있습니다.

class Point {
    public double x;
    public double y;

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }
}

Point a = new Point(5.0, 4.0);
Point b = new Point(4.0, 9.0);
double distance = Math.sqrt(Math.pow(b.x - a.x, 2) + Math.pow(b.y - a.y, 2));

깨끗한!

하지만 기억해:수업에는 행동이 없어야 할 뿐만 아니라 아니요 미래에도 행동을 할 이유가 있습니다.


(이것이 바로 이 답변 설명합니다.인용하려면 "Java 프로그래밍 언어에 대한 코드 규칙:10.프로그래밍 실습":

적절한 공용 인스턴스 변수의 한 가지 예는 클래스가 기본적으로 동작이 없는 데이터 구조인 경우입니다.즉, 만약 당신이 struct 클래스 대신(Java가 지원되는 경우 struct), 클래스의 인스턴스 변수를 공개로 설정하는 것이 적절합니다.

따라서 공식 문서에서도 이 관행을 허용합니다.)


또한 위의 구성원이 더욱 확실하다면 Point 클래스는 변경할 수 없어야 합니다. 그런 다음 추가할 수 있습니다. final 이를 시행하는 키워드:

public final double x;
public final double y;

그건 그렇고, 예제로 제공하는 구조는 이미 Java 기본 클래스 라이브러리에 다음과 같이 존재합니다. java.awt.Point.공개 필드로 x와 y가 있습니다. 직접 확인해 보세요.

자신이 무엇을 하고 있는지 알고 있고 팀의 다른 사람들도 이에 대해 알고 있다면 공개 필드를 사용해도 괜찮습니다.그러나 객체를 스택 할당 구조체인 것처럼 사용하는 개발자와 관련된 버그에서 골치 아픈 문제를 일으킬 수 있으므로 이에 의존해서는 안 됩니다(Java 객체는 항상 복사본이 아닌 참조로 메서드에 전송됩니다).

답장:아쿠, izb, 존 토플리...

가변성 문제에 주의하세요...

getter/setter를 생략하는 것이 합리적으로 보일 수 있습니다.어떤 경우에는 실제로 괜찮을 수도 있습니다.여기에 표시된 제안 패턴의 실제 문제는 변경 가능성입니다.

문제는 최종이 아닌 공개 필드를 포함하는 객체 참조를 전달하면 발생합니다.해당 참조가 포함된 다른 항목은 해당 필드를 자유롭게 수정할 수 있습니다.더 이상 해당 개체의 상태를 제어할 수 없습니다.(문자열이 변경 가능하다면 어떤 일이 일어날지 생각해 보세요.)

해당 개체가 다른 개체의 내부 상태의 중요한 부분인 경우 내부 구현이 노출된 경우 문제가 발생합니다.이를 방지하려면 대신 개체의 복사본을 반환해야 합니다.이는 효과가 있지만 생성된 수많은 일회용 복사본으로 인해 막대한 GC 부담이 발생할 수 있습니다.

공개 필드가 있는 경우 클래스를 읽기 전용으로 만드는 것을 고려하세요.필드를 생성자에 매개변수로 추가하고 필드를 최종으로 표시합니다.그렇지 않으면 내부 상태를 노출하지 않는지 확인하고, 반환 값에 대한 새 인스턴스를 생성해야 하는 경우 과도하게 호출되지 않는지 확인하세요.

보다:"효과적인 자바" 작성자: Joshua Bloch -- 항목 #13:불변성을 선호합니다.

추신:또한 요즘 모든 JVM은 가능하면 getMethod를 최적화하여 단일 필드 읽기 명령만 생성한다는 점을 명심하세요.

나는 getter와 setter가 의미상 무의미한 코드로 코드를 어수선하게 만들고 다른 언어는 규칙 기반 데이터 숨기기 또는 책임 분할(예:파이썬).

위에서 다른 사람들이 언급했듯이 두 가지 문제가 발생하며 실제로는 고칠 수 없습니다.

  • Java 세계의 거의 모든 자동화 도구는 getter/setter 규칙에 의존합니다.다른 사람들이 언급한 것처럼 jsp 태그, 스프링 구성, Eclipse 도구 등에 대해서도 마찬가지입니다.등...도구에서 기대하는 것과 싸우는 것은 봄 콩을 시작하는 비표준 방법을 찾으려고 Google을 통해 트롤링하는 긴 세션의 방법입니다.실제로 문제를 일으킬 가치가 없습니다.
  • 수백 개의 공개 변수가 포함된 우아하게 코딩된 애플리케이션이 있으면 불충분한 상황(불변성이 절대적으로 필요한 경우, 변수가 설정될 때 일부 이벤트를 트리거해야 하는 경우 또는 이벤트를 발생시키려는 경우)을 하나 이상 발견하게 될 것입니다. 객체 상태를 불쾌한 것으로 설정하기 때문에 변수 변경에 대한 예외입니다.그런 다음 변수가 직접 참조되는 모든 곳에서 특수 메서드를 사용하여 코드를 복잡하게 만드는 것, 애플리케이션의 1000개 변수 중 3개에 대한 특수 액세스 양식을 사용하는 것 사이에서 불가피한 선택을 해야 합니다.

그리고 이는 완전히 독립적인 개인 프로젝트에서 작업하는 최상의 시나리오입니다.모든 것을 공개적으로 접근 가능한 라이브러리로 내보내면 이러한 문제는 더욱 커질 것입니다.

Java는 매우 장황하며 이는 매력적인 일입니다.하지 마세요.

Java 방식이 OO 방식이라면 그렇습니다. 공개 필드가 있는 클래스를 생성하면 객체가 자체 내부 상태를 관리해야 한다는 정보 숨김에 대한 원칙이 깨집니다.(그래서 내가 단지 전문 용어를 뱉는 것이 아니기 때문에 정보 숨기기의 이점은 클래스의 내부 작동이 인터페이스 뒤에 숨겨져 있다는 것입니다. 구조체 클래스가 해당 필드 중 하나를 저장하는 메커니즘을 변경하고 싶다고 가정해 보겠습니다. 아마도 돌아가서 해당 클래스를 사용하는 모든 클래스를 변경해야 할 것입니다...)

또한 JavaBean 명명 호환 클래스에 대한 지원을 활용할 수 없습니다. 예를 들어 Expression Language를 사용하여 작성된 JavaServer Page에서 클래스를 사용하기로 결정하면 문제가 발생할 수 있습니다.

자바월드 기사 Getter 및 Setter 메서드가 나쁜 이유 접근자 및 변경자 메서드를 구현하지 말아야 할 경우에 대해 생각하는 데 도움이 될 수도 있는 기사입니다.

작은 솔루션을 작성하고 관련된 코드의 양을 최소화하려는 경우 Java 방식이 올바른 방법이 아닐 수 있습니다. 이는 항상 귀하와 귀하가 해결하려는 문제에 달려 있다고 생각합니다.

작성자가 제공하는 한 해당 유형의 코드에는 아무런 문제가 없습니다. 알고 있다 객체가 아닌 구조체(또는 데이터 셔틀)입니다.많은 Java 개발자는 잘 구성된 객체(java.lang.Object의 하위 클래스뿐만 아니라 진실 특정 도메인의 개체) 및 파인애플.따라서 그들은 객체가 필요할 때 구조체를 작성하게 되고 그 반대의 경우도 마찬가지입니다.

공개 필드 액세스를 사용할 때의 문제는 팩토리 메소드 대신 new를 사용하는 것과 동일한 문제입니다. 나중에 마음이 바뀌면 기존 호출자가 모두 손상됩니다.따라서 API 발전의 관점에서 볼 때 일반적으로 총알을 물고 getter/setter를 사용하는 것이 좋습니다.

내가 반대 방향으로 가는 곳은 예를 들어 내부 데이터 구조로 사용되는 내부 정적 클래스에서 클래스에 대한 액세스를 강력하게 제어하는 ​​경우입니다.이 경우 필드 액세스를 사용하는 것이 훨씬 더 명확할 수 있습니다.

그런데 e-bartek의 주장에 따르면 Java 7에 속성 지원이 추가될 가능성은 거의 없습니다.

나는 코드를 단순화하기 위해 비공개 내부 클래스를 구축할 때 이 패턴을 자주 사용하지만 공개 API에 그러한 객체를 노출하는 것은 권장하지 않습니다.일반적으로 공개 API의 객체를 불변으로 만드는 빈도가 높을수록 더 좋으며, '구조체와 같은' 객체를 불변 방식으로 구성하는 것은 불가능합니다.

여담이지만, 이 개체를 전용 내부 클래스로 작성하더라도 개체를 초기화하는 코드를 단순화하기 위해 생성자를 제공합니다.사용할 수 있는 객체를 얻기 위해 3줄의 코드를 작성해야 한다는 것은 정말 지저분한 일입니다.

매우 오래된 질문이지만 또 다른 짧은 기여를 하도록 하겠습니다.Java 8에는 람다 표현식과 메소드 참조가 도입되었습니다.람다 식은 간단한 메서드 참조일 수 있으며 "진정한" 본문을 선언하지 않을 수 있습니다.그러나 필드를 메서드 참조로 "변환"할 수는 없습니다.따라서

stream.mapToInt(SomeData1::x)

합법은 아니지만

stream.mapToInt(SomeData2::getX)

이다.

그것이 항상 단순한 구조체일 것이고 동작을 여기에 첨부하고 싶지 않을 것이라는 점을 안다면 나는 해를 보지 않습니다.

이것은 Java 언어가 아닌 객체 지향 설계에 대한 질문입니다.일반적으로 클래스 내의 데이터 유형을 숨기고 클래스 API의 일부인 메서드만 노출하는 것이 좋습니다.내부 데이터 유형을 노출하면 나중에 변경할 수 없습니다.이를 숨기면 사용자에 대한 유일한 의무는 메서드의 반환 및 인수 유형입니다.

여기서는 5명의 이름과 나이를 입력하고 선택 정렬(나이 기준)을 수행하는 프로그램을 만듭니다.나는 전체 작업을 수행하기 위해 구조(C 프로그래밍 언어와 같은) 역할을 하는 클래스와 메인 클래스를 사용했습니다.아래에서 코드를 제공하겠습니다 ...

import java.io.*;

class NameList {
    String name;
    int age;
}

class StructNameAge {
    public static void main(String [] args) throws IOException {

        NameList nl[]=new NameList[5]; // Create new radix of the structure NameList into 'nl' object
        NameList temp=new NameList(); // Create a temporary object of the structure

        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));

        /* Enter data into each radix of 'nl' object */

        for(int i=0; i<5; i++) {
            nl[i]=new NameList(); // Assign the structure into each radix

            System.out.print("Name: ");
            nl[i].name=br.readLine();

            System.out.print("Age: ");
            nl[i].age=Integer.parseInt(br.readLine());

            System.out.println();
        }

        /* Perform the sort (Selection Sort Method) */

        for(int i=0; i<4; i++) {
            for(int j=i+1; j<5; j++) {
                if(nl[i].age>nl[j].age) {
                    temp=nl[i];
                    nl[i]=nl[j];
                    nl[j]=temp;
                }
            }
        }

        /* Print each radix stored in 'nl' object */

        for(int i=0; i<5; i++)
            System.out.println(nl[i].name+" ("+nl[i].age+")");
    }
}

위의 코드는 오류가 없으며 테스트되었습니다 ...복사해서 IDE에 붙여넣기만 하면 됩니다.당신은 알고 무엇을???:)

Java에서는 메소드 없이 공개 필드만 사용하여 간단한 클래스를 만들 수 있지만 이는 여전히 클래스이고 구문적으로 처리되며 클래스와 마찬가지로 메모리 할당 측면에서 처리됩니다.Java에서는 구조체를 실제로 재현할 수 있는 방법이 없습니다.

때로는 메서드에서 여러 값을 반환해야 할 때 이러한 클래스를 사용합니다.물론 그러한 물체는 수명이 짧고 가시성이 매우 제한적이므로 괜찮습니다.

대부분의 경우와 마찬가지로 일반적인 규칙이 있고 특정 상황이 있습니다.주어진 객체가 어떻게 사용될지 알기 위해 폐쇄적이고 캡처된 애플리케이션을 수행하는 경우 가시성 및/또는 효율성을 선호하기 위해 더 많은 자유를 행사할 수 있습니다.당신이 통제할 수 없는 다른 사람들이 공개적으로 사용할 클래스를 개발하는 경우 getter/setter 모델을 사용하십시오.모든 것과 마찬가지로 상식을 사용하십시오.대중과 함께 초기 라운드를 수행한 다음 나중에 getter/setter로 변경하는 것이 괜찮은 경우가 많습니다.

관점 지향 프로그래밍을 사용하면 할당이나 가져오기를 트랩하고 여기에 가로채는 논리를 연결할 수 있습니다. 이것이 문제를 해결하는 올바른 방법이라고 제안합니다.(공개되어야 하는지, 보호되어야 하는지, 패키지 보호되어야 하는지에 대한 문제는 직교합니다.)

따라서 올바른 액세스 한정자를 사용하여 방해받지 않는 필드로 시작합니다.프로그램 요구 사항이 증가함에 따라 유효성을 검사하고 반환되는 개체의 복사본을 만드는 등의 논리를 추가할 수 있습니다.

getter/setter 철학은 필요하지 않은 다수의 간단한 경우에 비용을 부과합니다.

측면 스타일이 더 깨끗한지 여부는 다소 질적입니다.클래스의 변수만 보고 로직을 별도로 보는 것이 쉽다는 것을 알았습니다.사실, Apect 지향 프로그래밍의 존재 이유는 많은 우려 사항이 교차 절단되고 클래스 본문 자체에서 그것들을 구분하는 것이 이상적이지 않다는 것입니다(로깅이 예가 됩니다. - Java가 원하는 모든 것을 기록하려는 경우) 여러 getter를 작성하고 동기화를 유지하지만 AspectJ에서는 한 줄만 사용할 수 있습니다.

IDE의 문제는 청어입니다.get/set에서 발생하는 읽기 및 시각적 오염만큼 타이핑이 중요하지 않습니다.

주석은 얼핏 보면 관점 지향 프로그래밍과 유사해 보이지만 AspectJ의 간결한 와일드카드와 같은 포인트컷 사양과 달리 주석을 첨부하여 포인트컷을 철저하게 열거해야 합니다.

AspectJ에 대한 인식이 사람들이 동적 언어에 성급하게 정착하는 것을 방지해주기를 바랍니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top