Pergunta

É completamente contra a maneira Java de criar objetos semelhantes a estruturas?

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

Posso ver uma classe com acessadores e modificadores sendo mais parecida com Java.

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

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

    private int x;
    private int y;
}

A classe do primeiro exemplo é notavelmente conveniente.

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

Isto não é tão conveniente.

// a function in a class
public int f(SomeData2 d) {
    return (3 * d.getX()) / d.getY();
}
Foi útil?

Solução

Este é um tópico comumente discutido.A desvantagem de criar campos públicos em objetos é que você não tem controle sobre os valores definidos para eles.Em projetos de grupo onde há muitos programadores utilizando o mesmo código, é importante evitar efeitos colaterais.Além disso, às vezes é melhor retornar uma cópia do objeto do campo ou transformá-lo de alguma forma, etc.Você pode simular esses métodos em seus testes.Se você criar uma nova classe, talvez não veja todas as ações possíveis.É como programação defensiva - algum dia getters e setters podem ser úteis e não custa muito criá-los/utilizá-los.Então, às vezes, eles são úteis.

Na prática, a maioria dos campos possui getters e setters simples.Uma possível solução seria assim:

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

Atualizar:É altamente improvável que o suporte a propriedades seja adicionado no Java 7 ou talvez nunca.Outras linguagens JVM como Groovy, Scala, etc. suportam esse recurso agora.-Alex Miller

Outras dicas

Parece que muitas pessoas Java não estão familiarizadas com as diretrizes de codificação Sun Java, que dizem que é bastante apropriado usar a variável de instância pública quando a classe é essencialmente uma "estrutura", se Java suportou "Struct" (quando não houver comportamento).

As pessoas tendem a pensar que getters e setters são o caminho java, como se estivessem no coração de Java.Isto não é assim.Se você seguir as diretrizes de codificação Sun Java, usando variáveis ​​de instância pública em situações apropriadas, você está realmente escrevendo um código melhor do que desordenando -o com getters e setters desnecessários.

Convenções de código Java de 1999 e ainda inalterado.

10.1 Fornecendo acesso a variáveis ​​de instância e classe

Não torne pública nenhuma instância ou variável de classe sem um bom motivo.Muitas vezes, as variáveis ​​de instância não precisam ser explicitamente definidas ou obtidas - muitas vezes isso acontece como um efeito colateral das chamadas de método.

Um exemplo de variáveis ​​de instância pública apropriadas é o caso em que a classe é essencialmente uma estrutura de dados, sem comportamento. Em outras palavras, se você tivesse usado uma struct em vez de uma classe (se Java suportasse struct), então seria apropriado tornar públicas as variáveis ​​de instância da classe.

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

Use o bom senso realmente.Se você tiver algo como:

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

Então não faz sentido envolvê-los em getters e setters.Você nunca armazenará uma coordenada x, y em pixels inteiros de outra maneira.Getters e setters apenas irão atrasá-lo.

Por outro lado, com:

public class BankAccount{
    public int balance;
}

Você pode querer alterar a forma como um saldo é calculado em algum momento no futuro.Isso realmente deveria usar getters e setters.

É sempre preferível saber por que você está aplicando boas práticas, para saber quando pode quebrar as regras.

Para resolver questões de mutabilidade, você pode declarar x e y como finais.Por exemplo:

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

Chamar o código que tenta gravar nesses campos obterá um erro em tempo de compilação de "campo x é declarado final;não pode ser atribuído".

O código do cliente pode então ter a conveniência 'abreviada' que você descreveu em sua postagem

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();
    }
}

Não use public Campos

Não use public campos quando você realmente deseja agrupar o comportamento interno de uma classe.Pegar java.io.BufferedReader por exemplo.Possui o seguinte campo:

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

skipLF é lido e escrito em todos os métodos de leitura.E se uma classe externa executada em um thread separado modificasse maliciosamente o estado do skipLF no meio de uma leitura? BufferedReader definitivamente vai dar errado.

Encharcar public Campos

Pegue isso Point classe por exemplo:

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;
    }
}

Isso tornaria o cálculo da distância entre dois pontos muito difícil de escrever.

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));

A classe não possui nenhum comportamento além de getters e setters simples.É aceitável utilizar campos públicos quando a classe representa apenas uma estrutura de dados, e não possui, e nunca terá comportamento (thin getters e setters é não comportamento considerado aqui).Pode ser escrito melhor assim:

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));

Limpar!

Mas lembre-se:Não apenas sua turma deve estar ausente de comportamento, mas também deve ter não razão para ter comportamento no futuro também.


(Isso é exatamente o que esta resposta descreve.Citar "Convenções de código para a linguagem de programação Java:10.Práticas de programação":

Um exemplo de variáveis ​​de instância pública apropriadas é o caso em que a classe é essencialmente uma estrutura de dados, sem comportamento.Em outras palavras, se você tivesse usado um struct em vez de uma classe (se Java for compatível struct), então é apropriado tornar públicas as variáveis ​​de instância da classe.

Portanto a documentação oficial também aceita esta prática.)


Além disso, se você tiver certeza de que os membros acima Point classe deve ser imutável, então você pode adicionar final palavra-chave para aplicá-lo:

public final double x;
public final double y;

A propósito, a estrutura que você está dando como exemplo já existe na biblioteca de classes base Java como java.awt.Point.Tem x e y como campos públicos, Confira por si mesmo.

Se você sabe o que está fazendo e outras pessoas da sua equipe sabem disso, não há problema em ter campos públicos.Mas você não deve confiar nisso porque eles podem causar dores de cabeça como em bugs relacionados a desenvolvedores que usam objetos como se fossem structs alocadas em pilha (objetos java são sempre enviados para métodos como referências e não como cópias).

Ré:aku, izb, John Topley...

Cuidado com problemas de mutabilidade...

Pode parecer sensato omitir getters/setters.Na verdade, pode estar tudo bem em alguns casos.O verdadeiro problema com o padrão proposto mostrado aqui é a mutabilidade.

O problema é quando você passa uma referência de objeto contendo campos públicos não finais.Qualquer outra coisa com essa referência está livre para modificar esses campos.Você não tem mais controle sobre o estado desse objeto.(Pense no que aconteceria se Strings fossem mutáveis.)

Fica ruim quando esse objeto é uma parte importante do estado interno de outro, você acabou de expor a implementação interna.Para evitar isso, uma cópia do objeto deve ser retornada.Isso funciona, mas pode causar enorme pressão no GC devido às toneladas de cópias descartáveis ​​criadas.

Se você tiver campos públicos, considere tornar a classe somente leitura.Adicione os campos como parâmetros ao construtor e marque os campos como finais.Caso contrário, certifique-se de não expor o estado interno e, se precisar construir novas instâncias para um valor de retorno, certifique-se de que ele não será chamado excessivamente.

Ver:"Java eficaz"por Joshua Bloch - Item nº 13:Favorece a Imutabilidade.

PS:Lembre-se também de que todas as JVMs atualmente otimizarão o getMethod, se possível, resultando em apenas uma única instrução de leitura de campo.

Eu tentei isso em alguns projetos, com base na teoria de que getters e setters bagunçam o código com lixo semanticamente sem sentido, e que outras linguagens parecem se dar bem com ocultação de dados baseada em convenções ou particionamento de responsabilidades (por exemplo,Pitão).

Como outros observaram acima, você encontra dois problemas e eles não podem ser corrigidos:

  • Praticamente qualquer ferramenta automatizada no mundo Java depende da convenção getter/setter.O mesmo vale para, como observado por outros, tags jsp, configuração de primavera, ferramentas de eclipse, etc.etc...Lutar contra o que suas ferramentas esperam ver é uma receita para longas sessões vasculhando o Google tentando encontrar aquela maneira fora do padrão de iniciar os feijões de primavera.Realmente não vale a pena.
  • Depois de ter seu aplicativo elegantemente codificado com centenas de variáveis ​​públicas, você provavelmente encontrará pelo menos uma situação em que elas são insuficientes - onde você absolutamente precisa de imutabilidade ou precisa acionar algum evento quando a variável for definida ou deseja lançar uma exceção em uma mudança de variável porque define o estado de um objeto como algo desagradável.Você fica então preso às escolhas nada invejáveis ​​entre sobrecarregar seu código com algum método especial em todos os lugares em que a variável é referenciada diretamente, tendo algum formulário de acesso especial para 3 das 1000 variáveis ​​em seu aplicativo.

E este é o melhor cenário de trabalhar inteiramente em um projeto privado independente.Depois de exportar tudo para uma biblioteca acessível ao público, esses problemas se tornarão ainda maiores.

Java é muito detalhado e isso é algo tentador de se fazer.Não faça isso.

Se o jeito Java é o jeito OO, então sim, criar uma classe com campos públicos quebra os princípios em torno da ocultação de informações que dizem que um objeto deve gerenciar seu próprio estado interno.(Então, como não estou apenas falando jargão para você, um benefício da ocultação de informações é que o funcionamento interno de uma classe fica oculto atrás de uma interface - digamos que você queira alterar o mecanismo pelo qual sua classe struct salvou um de seus campos, você provavelmente precisará voltar e alterar qualquer classe que use a classe...)

Você também não pode aproveitar as vantagens do suporte para classes compatíveis com nomenclatura JavaBean, o que será prejudicial se você decidir, por exemplo, usar a classe em uma página JavaServer escrita usando Expression Language.

O artigo JavaWorld Por que os métodos Getter e Setter são maus Este artigo também pode ser do seu interesse ao pensar sobre quando não implementar métodos acessadores e modificadores.

Se você está escrevendo uma solução pequena e deseja minimizar a quantidade de código envolvida, o método Java pode não ser o correto - acho que sempre depende de você e do problema que está tentando resolver.

Não há nada de errado com esse tipo de código, desde que o autor sabe eles são estruturas (ou transportes de dados) em vez de objetos.Muitos desenvolvedores Java não conseguem diferenciar um objeto bem formado (não apenas uma subclasse de java.lang.Object, mas um verdadeiro objeto em um domínio específico) e um abacaxi.Portanto, eles acabam escrevendo estruturas quando precisam de objetos e vice-versa.

O problema de usar o acesso ao campo público é o mesmo que usar o método new em vez do método de fábrica - se você mudar de ideia mais tarde, todos os chamadores existentes serão interrompidos.Portanto, do ponto de vista da evolução da API, geralmente é uma boa ideia se arriscar e usar getters/setters.

Um lugar onde vou na direção oposta é quando você controla fortemente o acesso à classe, por exemplo, em uma classe estática interna usada como uma estrutura de dados interna.Neste caso, pode ser muito mais claro usar o acesso ao campo.

A propósito, de acordo com a afirmação do e-bartek, é altamente improvável, na IMO, que o suporte à propriedade seja adicionado no Java 7.

Frequentemente uso esse padrão ao criar classes internas privadas para simplificar meu código, mas não recomendaria expor esses objetos em uma API pública.Em geral, quanto mais frequentemente você tornar imutáveis ​​os objetos em sua API pública, melhor, e não é possível construir seu objeto 'semelhante a uma estrutura' de maneira imutável.

Além disso, mesmo se eu estivesse escrevendo este objeto como uma classe interna privada, ainda forneceria um construtor para simplificar o código para inicializar o objeto.Ter que ter 3 linhas de código para obter um objeto utilizável quando for necessário é simplesmente confuso.

Uma pergunta muito antiga, mas deixe-me fazer outra breve contribuição.Java 8 introduziu expressões lambda e referências de métodos.Expressões lambda podem ser referências de métodos simples e não declarar um corpo "verdadeiro".Mas você não pode “converter” um campo em uma referência de método.Por isso

stream.mapToInt(SomeData1::x)

não é legal, mas

stream.mapToInt(SomeData2::getX)

é.

Não vejo mal nenhum se você souber que sempre será uma estrutura simples e que nunca desejará anexar um comportamento a ela.

Esta é uma questão sobre Design Orientado a Objetos, não sobre Java, a linguagem.Geralmente é uma boa prática ocultar os tipos de dados dentro da classe e expor apenas os métodos que fazem parte da API da classe.Se você expor tipos de dados internos, nunca poderá alterá-los no futuro.Se você os ocultar, sua única obrigação para com o usuário será o retorno e os tipos de argumento do método.

Aqui eu crio um programa para inserir o nome e a idade de 5 pessoas diferentes e realizar uma classificação de seleção (idade).Usei uma classe que funciona como estrutura (como a linguagem de programação C) e uma classe principal para realizar a operação completa.A seguir estou fornecendo o código ...

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+")");
    }
}

O código acima está livre de erros e testado ...Basta copiar e colar em seu IDE e ...Você sabe e o que???:)

Você pode criar uma classe simples com campos públicos e sem métodos em Java, mas ela ainda é uma classe e ainda é tratada sintaticamente e em termos de alocação de memória como uma classe.Não há como reproduzir estruturas genuinamente em Java.

Às vezes eu uso essa classe quando preciso retornar vários valores de um método.É claro que tal objeto tem vida curta e visibilidade muito limitada, então deve estar OK.

Tal como acontece com a maioria das coisas, existe a regra geral e existem circunstâncias específicas.Se você estiver fazendo uma aplicação fechada e capturada para saber como um determinado objeto será utilizado, poderá exercer mais liberdade para favorecer a visibilidade e/ou eficiência.Se você estiver desenvolvendo uma classe que será usada publicamente por outras pessoas além do seu controle, incline-se para o modelo getter/setter.Como acontece com todas as coisas, basta usar o bom senso.Muitas vezes não há problema em fazer uma rodada inicial com públicos e depois alterá-los para getter/setters.

A programação orientada a aspectos permite capturar atribuições ou buscas e anexar lógica de interceptação a elas, o que proponho ser o caminho certo para resolver o problema.(A questão de saber se eles deveriam ser públicos, protegidos ou protegidos por pacote é ortogonal.)

Assim, você começa com campos não interceptados com o qualificador de acesso correto.À medida que os requisitos do seu programa aumentam, você anexa lógica para talvez validar, fazer uma cópia do objeto que está sendo retornado, etc.

A filosofia getter/setter impõe custos a um grande número de casos simples onde eles não são necessários.

Se o estilo de aspecto é mais limpo ou não, é algo qualitativo.Eu acharia fácil ver apenas as variáveis ​​em uma classe e visualizar a lógica separadamente.Na verdade, a razão de ser da programação orientada ao Apect é que muitas preocupações são transversais e compartimentá-las no corpo da classe em si não é o ideal (o log é um exemplo - se você quiser registrar todos os resultados que o Java deseja que você faça escreva um monte de getters e mantenha-os sincronizados, mas o AspectJ permite uma linha única).

A questão do IDE é uma pista falsa.Não é tanto a digitação, mas a poluição visual e de leitura que surge de get/sets.

À primeira vista, as anotações parecem semelhantes à programação orientada a aspectos, no entanto, exigem que você enumere exaustivamente os pontos de corte anexando anotações, em oposição a uma especificação de corte de pontos concisa, semelhante a um curinga, no AspectJ.

Espero que o conhecimento do AspectJ evite que as pessoas optem prematuramente por linguagens dinâmicas.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top