O que é uma invariante de classe em java?


93

Eu pesquisei o tópico, mas além da Wikipedia não encontrei nenhuma documentação ou artigo útil.

Alguém pode me explicar em palavras simples o que significa ou indicar alguma documentação agradável e fácil de entender?


2
+1 para a pergunta porque a página da Wikipedia tem um ótimo exemplo de algo que eu não sabia que você poderia fazer - tem até exemplos. A explicação deles é melhor do que eu poderia fazer por você; é muito simples.
iandismo de


Se você estiver interessado em invariantes em Java, talvez esteja interessado em contratos para Java .
Mike Samuel

Respostas:


91

Isso não significa nada em particular em referência a java.

Uma invariante de classe é simplesmente uma propriedade válida para todas as instâncias de uma classe, sempre, não importa o que outro código faça.

Por exemplo,

class X {
  final Y y = new Y();
}

X tem a classe invariante de que existe uma ypropriedade e nunca é nulle tem um valor de tipo Y.

class Counter {
  private int x;

  public int count() { return x++; }
}

falha em manter dois invariantes importantes

  1. Isso countnunca retorna um valor negativo devido a um possível estouro negativo.
  2. Essas chamadas countestão aumentando estritamente monotonicamente.

A classe modificada preserva essas duas invariantes.

class Counter {
  private int x;

  public synchronized int count() {
    if (x == Integer.MAX_VALUE) { throw new IllegalStateException(); }
    return x++;
  }
}

mas falha em preservar o invariante que chama para countsempre ter sucesso normalmente (violações de TCB ausentes ) porque countpode lançar uma exceção ou pode bloquear se um thread em conflito possuir o monitor do contador.

Cada linguagem com classes torna mais fácil manter algumas invariantes de classe, mas não outras. Java não é exceção:

  1. As classes Java têm ou não têm propriedades e métodos consistentemente, portanto, as invariantes de interface são fáceis de manter.
  2. As classes Java podem proteger seus private campos, portanto, invariantes que dependem de dados privados são fáceis de manter.
  3. As classes Java podem ser finais, portanto, as invariantes que dependem da inexistência de código que viole uma invariante criando uma subclasse maliciosa podem ser mantidas.
  4. Java permite que os nullvalores se infiltrem de várias maneiras, por isso é difícil manter invariantes "tem um valor real".
  5. Java tem threads, o que significa que as classes que não sincronizam têm problemas para manter invariantes que dependem de operações sequenciais em uma thread acontecendo juntas.
  6. Java tem exceções que tornam fácil manter invariantes como "retorna um resultado com propriedade p ou não retorna nenhum resultado", mas é mais difícil manter invariantes como "sempre retorna um resultado".

† - Uma externalidade ou violação de TCB é um evento que um projetista de sistemas assume, de maneira otimista, que não acontecerá.

Normalmente, apenas confiamos que o hardware básico funciona conforme anunciado quando falamos sobre propriedades de linguagens de alto nível construídas sobre eles, e nossos argumentos que as invariantes mantêm não levam em consideração a possibilidade de:

  • Um programador usando ganchos de depuração para alterar variáveis ​​locais enquanto um programa é executado de maneiras que o código não pode.
  • Seus pares não usam reflexão com setAccessiblepara modificar privatetabelas de pesquisa.
  • Loki alterando a física, fazendo com que seu processador compare incorretamente dois números.

Para alguns sistemas, nosso TCB pode incluir apenas partes do sistema, portanto, não podemos assumir que

  • Um administrador ou daemon privilegiado não matará nosso processo JVM,

mas podemos supor que

  • Podemos verificar um sistema de arquivos transacional confiável.

Quanto mais alto nível um sistema, maior normalmente é seu TCB, mas quanto mais coisas não confiáveis ​​você pode obter de seu TCB, mais provável é que suas invariantes sejam retidas e mais confiável seu sistema será a longo prazo.


1
É "que countnunca retorna o mesmo valor duas vezes" realmente considerado uma invariante de classe?
ruakh

@ruakh, essa é uma boa pergunta. Eu não tenho tanta certeza. Coisas como estabilidade de hashCode (para cada instância i, i.hashCode () não muda) são freqüentemente chamadas de invariantes de classe, o que requer raciocínio sobre os valores retornados anteriormente, então parece razoável dizer que "para cada instância i, i.count () not in (resultados anteriores de i.count ()) "é uma classe invariante.
Mike Samuel

@ruakh Isso não é uma coisa de definição pura? Se eu postular tal invariante, por que não? Certamente pode ser uma garantia interessante e importante (digamos, para gerar IDs exclusivos). Pessoalmente, também acho que algo como "se apenas um código de thread único acessa essa classe, as seguintes propriedades serão mantidas" é útil, mas não tenho certeza se é possível estender a definição de tal forma que ela só tenha que ser mantida enquanto certa as condições são verdadeiras. (E considerando a reflexão, é basicamente impossível garantir algo interessante de outra forma!)
Voo

1
@ruakh - você pode modelá-lo como uma invariante de classe ou invariante de método. De qualquer forma, a modelagem requer um histórico conceitual de chamadas anteriores ao método em um objeto específico. Na verdade, você pode até modelar isso como uma pós-condição do método; isto é, o valor do resultado é aquele que não foi retornado anteriormente.
Stephen C

@Voo: Re: "Isso não é uma definição pura?": Certamente, mas como a questão aqui é: "O que é uma 'invariante de classe'?", Acho que a definição é 100% relevante. Parece preferível, na medida do possível, usar exemplos claros, ou então chamar explicitamente casos de não-definição clara. (A propósito, não estou discordando ativamente do exemplo; fiquei apenas surpreso com ele e estava perguntando para ter certeza.)
ruakh

20

Invariante significa algo que deve obedecer às suas condições, independentemente das alterações ou de quem o usa / transforma. Ou seja, uma propriedade de uma classe sempre cumpre ou satisfaz alguma condição, mesmo depois de passar por transformações usando métodos públicos. Assim, o cliente ou usuário desta classe está assegurado sobre a classe e sua propriedade.

Por exemplo,

  1. a condição no argumento da função é que deve ser sempre> 0 (maior que zero) ou não deve ser nulo.
  2. A propriedade minimum_account_balance de uma classe de conta indica, não pode ir abaixo de 100. Portanto, todas as funções públicas devem respeitar esta condição e garantir a invariante de classe.
  3. dependência baseada em regras entre variáveis, isto é, o valor de uma variável depende de outra, então se uma muda, usando alguma regra de correção, outra também deve mudar. Esta relação entre 2 variáveis ​​deve ser preservada. Caso contrário, o invariante é violado.

11

Eles são fatos que devem ser verdadeiros sobre uma classe de instância. Por exemplo, se uma classe tem uma propriedade X e invariante pode ser X deve ser maior que 0. Pelo que eu sei, não há nenhum método embutido para manter invariáveis, você deve tornar as propriedades privadas e certificar-se de que seus getters e setters cumpram a propriedade de invariância.

Existem anotações disponíveis que podem verificar as propriedades usando reflexão e interceptores. http://docs.oracle.com/javaee/7/api/javax/validation/constraints/package-summary.html

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.