Os métodos auxiliares particulares devem ser estáticos se puderem ser estáticos

205

Digamos que eu tenha uma classe projetada para ser instanciada. Eu tenho vários métodos "auxiliares" particulares dentro da classe que não requerem acesso a nenhum membro da classe e operam apenas em seus argumentos, retornando um resultado.

public class Example {
   private Something member;

   public double compute() {
       double total = 0;
       total += computeOne(member);
       total += computeMore(member);
       return total;         
   }

   private double computeOne(Something arg) { ... }
   private double computeMore(Something arg) {... } 
} 

Existe algum motivo específico para especificar computeOnee computeMorecomo métodos estáticos - ou algum motivo específico para não?

É certamente mais fácil deixá-los como não estáticos, mesmo que eles possam certamente ser estáticos sem causar problemas.

avalys
fonte
3
Consulte também quando não usar a palavra-chave static em Java: stackoverflow.com/questions/1766715/…
Mark Butler

Respostas:

174

Eu prefiro que tais métodos auxiliares sejam private static; o que deixará claro ao leitor que eles não modificarão o estado do objeto. Meu IDE também mostrará chamadas para métodos estáticos em itálico, portanto, saberei que o método é estático sem procurar a assinatura.

Esko Luontola
fonte
2
Estou usando o NetBeans e mostra chamadas de método estático com fontes em itálico.
Skiabox 28/06/12
2
Normalmente, declaro esses métodos auxiliares protected staticque os tornam testáveis ​​com muita facilidade em uma classe de teste no mesmo pacote.
James
2
@ James ou simplesmente static(se quisermos apenas para testar).
Mrod 08/03/19
3
"Não modificará o estado do objeto" - explicação perfeita de como diferenciá-lo de um método privado.
HopeKing
110

Isso pode resultar em bytecode um pouco menor, já que os métodos estáticos não terão acesso this. Não acho que isso faça diferença na velocidade (e, se o fizesse, provavelmente seria pequeno demais para fazer a diferença no geral).

Eu os tornaria estáticos, pois geralmente faço isso, se possível. Mas sou só eu.


EDIT: Esta resposta está ficando com voto negativo, possivelmente por causa da afirmação infundada sobre o tamanho do bytecode. Então, eu realmente vou fazer um teste.

class TestBytecodeSize {
    private void doSomething(int arg) { }
    private static void doSomethingStatic(int arg) { }
    public static void main(String[] args) {
        // do it twice both ways
        doSomethingStatic(0);
        doSomethingStatic(0);
        TestBytecodeSize t = new TestBytecodeSize();
        t.doSomething(0);
        t.doSomething(0);
    }
}

Bytecode (recuperado com javap -c -private TestBytecodeSize):

Compiled from "TestBytecodeSize.java"
class TestBytecodeSize extends java.lang.Object{
TestBytecodeSize();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

private void doSomething(int);
  Code:
   0:   return

private static void doSomethingStatic(int);
  Code:
   0:   return

public static void main(java.lang.String[]);
  Code:
   0:   iconst_0
   1:   invokestatic    #2; //Method doSomethingStatic:(I)V
   4:   iconst_0
   5:   invokestatic    #2; //Method doSomethingStatic:(I)V
   8:   new     #3; //class TestBytecodeSize
   11:  dup
   12:  invokespecial   #4; //Method "<init>":()V
   15:  astore_1
   16:  aload_1
   17:  iconst_0
   18:  invokespecial   #5; //Method doSomething:(I)V
   21:  aload_1
   22:  iconst_0
   23:  invokespecial   #5; //Method doSomething:(I)V
   26:  return

}

A invocação do método estático requer dois bytecodes (byteops?): iconst_0(Para o argumento) e invokestatic.
A invocação do método não estático requer três: aload_1(para o TestBytecodeSizeobjeto, suponho), iconst_0(para o argumento) e invokespecial. (Observe que, se esses não fossem métodos particulares, teria sido em invokevirtualvez de invokespecial; consulte JLS §7.7 Invocando métodos .)

Agora, como eu disse, não espero que haja grande diferença no desempenho entre esses dois, além do fato de invokestaticexigir menos um bytecode. invokestatice invokespecialambos devem ser um pouco mais rápidos que invokevirtual, uma vez que ambos usam ligação estática em vez de dinâmica, mas não faço ideia se um deles é mais rápido que o outro. Também não consigo encontrar boas referências. O mais próximo que posso encontrar é este artigo do JavaWorld de 1997 , que basicamente reafirma o que eu acabei de dizer:

As instruções mais rápidas provavelmente serão invokespeciale invokestatic, porque os métodos invocados por essas instruções são estaticamente vinculados. Quando a JVM resolve a referência simbólica para essas instruções e a substitui por uma referência direta, essa referência direta provavelmente incluirá um ponteiro para os bytecodes reais.

Mas muitas coisas mudaram desde 1997.

Então, em conclusão ... acho que ainda estou mantendo o que disse antes. A velocidade não deve ser o motivo para escolher um sobre o outro, pois seria na melhor das hipóteses uma micro-otimização.

Michael Myers
fonte
todos esses votos negativos para o que parece ser uma resposta muito direta. +1 no interesse da verdade, da justiça, ....
Steve B.
Obrigado. (Embora eu poderia ter excluído-lo e obteve um crachá enquanto ele estava em -3:. D)
Michael Myers
2
De qualquer forma, o compilador JIT os alinhará e os otimizará, portanto a quantidade de instruções de bytecode tem pouco a ver com a rapidez com que será.
Esko Luontola
3
Por isso eu disse: "Não acho que faça diferença na velocidade (e, se o fizesse, provavelmente seria pequeno demais para fazer a diferença no geral)".
Michael Myers
7
Antes de se envolver em qualquer micro otimização desse tipo, considere o efeito do compilador de hot spot. Bottom line: escrever o código para facilitar a leitura por seres humanos e deixar otimização para o compilador
Ichthyo
18

Minha preferência pessoal seria declará-los estáticos, pois é uma bandeira clara que eles são apátridas.

Steve B.
fonte
18

A resposta é ... depende.

Se member é uma variável de instância específica para o objeto com o qual você está lidando, por que passá-la como parâmetro?

Por exemplo:

public class Example {
   private Something member;

   public double compute() {
       double total = 0;
       total += computeOne();
       total += computeMore();
       return total;         
   }

   private double computeOne() { /* Process member here */ }
   private double computeMore() { /* Process member here */ } 
}
Powerlord
fonte
5
+1: Embora o exemplo seja ruim, muitos métodos que PODEM ser estáticos são um cheiro ruim para o código. A propósito, métodos estáticos são na verdade funções, eles não são absolutamente OO. Eles podem pertencer como um método em outra classe ou você pode estar faltando uma classe à qual essa função possa pertencer como método.
Bill K
11

Um motivo pelo qual você pode querer declarar métodos auxiliares estáticos é se precisar chamá-los no construtor da classe "before" thisou super. Por exemplo:

public class MyClass extends SomeOtherClass { 
    public MyClass(String arg) {
       super(recoverInt(arg));
    }

    private static int recoverInt(String arg) {
       return Integer.parseInt(arg.substring(arg.length() - 1));
    }
}

Este é um exemplo um pouco artificial, mas claramente recoverIntnão pode ser um método de instância nesse caso.

oxbow_lakes
fonte
você pode chamar um método de instância no construtor, mas se estiver delegando para outro construtor com super (...) ou este (...), não poderá chamar um método de instância até depois da delegação (mas as estáticas são ok como você apontar)
Kip
10

Eu realmente não consigo pensar em vantagens claras para o método estático privado. Dito isto, também não há vantagens específicas em torná-los não estáticos. É principalmente uma questão de apresentação: você pode torná-los estáticos para sublinhar claramente o fato de que eles não estão alterando um objeto.

Para o método com privilégios de acesso diferentes, acho que existem dois argumentos principais:

  • métodos estáticos podem ser chamados sem criar uma instância de um objeto, o que pode ser útil
  • métodos estáticos não podem ser herdados, o que pode ser um problema se você precisar de polimorfismo (mas é irrelevante para métodos particulares).

Além disso, a diferença é bem pequena, e duvido muito que o extra desse ponteiro passado para o método de instância faça uma diferença significativa.

Axelle Ziegler
fonte
4
A vantagem de não torná-lo estático é que alguém pode subclassificar e substituir o comportamento do método.
Clint Miller
7
Clint é um método privado para que você não possa substituir o método.
Bhushan Bhangale 12/02/09
Exatamente, para o método privado, não acho que faça muita diferença. Para métodos públicos, no entanto, concordo com o que você está dizendo
Axelle Ziegler
8

A resposta correta é:

qualquer método que não obtenha nenhuma informação de um campo e não coloque nenhuma informação em um campo não precisa ser um método de instância. Qualquer método que não use ou altere nenhum campo em sua classe ou objeto também pode ser um método estático.

Gyan Singh
fonte
5

ou algum motivo em particular para não os declarar estáticos?

Sim.

Mantendo-os como métodos de instância, você se permite fornecer uma implementação diferente posteriormente.

Pode parecer bobagem (e, na verdade, seria se esses métodos fossem usados ​​apenas por você em um programa de 50 linhas), mas em aplicativos maiores ou em bibliotecas usadas por outra pessoa, você pode optar por escolher uma implementação melhor, mas não deseja quebrar o código existente.

Então você cria uma subclasse e retorna isso nas novas versões e, como os métodos foram declarados como métodos de instância, você apenas deixou o polimorfismo fazer seu trabalho.

Além disso, você pode se beneficiar ao tornar o construtor privado e fornecer um método estático de fábrica pelo mesmo motivo.

Portanto, minha recomendação é mantê-los como métodos de instância e evitar estática, se possível.
Aproveite o dinamismo que a linguagem oferece.

Veja aqui um vídeo relacionado: Como criar uma boa API e por que ela é importante

Embora não esteja diretamente relacionado à discussão do método "static vs instance", ele aborda alguns pontos interessantes no design da API.

OscarRyz
fonte
1
Totalmente de acordo. Eu costumo tentar evitar estática e privates por esse motivo. Eu acho que a maioria das outras respostas errou o ponto.
Clint Miller
22
Estamos falando de ajudantes particulares , o que significa que eles não fazem parte da API pública da classe. Isso significa que nada impede que você forneça uma implementação diferente posteriormente, tornando-a não estática ou fazendo outras alterações.
Jonik
Aaaahmm ... esse é um bom argumento, Jonik. Ainda assim, criar um bom hábito deve ser motivo suficiente (subjetivamente falando, é claro) #
OscarRyz 12/02/09
2
Não concordo completamente. Não há uma boa razão para você querer alterar um método privado sem alterar a classe na qual ele está definido. A única maneira de fazer o que você sugere é pela manipulação do código de bytes.
12119 Peter Lawrey
2
O que você sugere pode fazer sentido se o método não for privado, mas mesmo assim eu sugeriria que você projetasse com base na corrente necessária, em vez de projetar com base na necessidade imaginada.
12119 Peter Lawrey
5

Uma questão sobre ter métodos estáticos é que ele pode dificultar o uso do objeto em testes de unidade . O Mockito não pode criar simulações para métodos estáticos e você não pode criar uma implementação de subclasse do método.

Jon Onstott
fonte
2
Você não deve escrever testes para métodos privados. Veja aqui . Não há necessidade de testar um método privado, seja ele estático ou não.
BW
@ BB Isso é uma coisa muito subjetiva. Existem muitos motivos válidos para testar um método privado.
Ruohola 17/09/19
4

Se o método for basicamente apenas uma sub-rotina que nunca usará previsivelmente as informações de estado, declare-as estáticas.

Isso permite que ele seja usado em outros métodos estáticos ou na inicialização de classes, ou seja:

public class Example {
   //...

   //Only possible if computeOne is static
   public final static double COMPUTED_ONE = computeOne(new Something("1"));

   //...
}
Kip
fonte
3

A questão estática / não estática se resume a "precisarei realmente usar um objeto dessa classe"?

Então, você está passando o objeto entre diferentes métodos? O objeto contém informações úteis fora do contexto dos métodos estáticos? Existe alguma razão para não definir métodos nos dois sentidos, se você os usar nos dois sentidos?

Se você está nesse dilema, parece-me que você tem todos os dados necessários para o método flutuando no seu código fora do objeto. É isso que voce quer? Seria mais fácil sempre coletar sempre esses dados em um objeto a cada vez? Você pode ser ambivalente em se comprometer com um único modelo. Se você puder fazer tudo de uma maneira, escolha estática ou não estática e siga em frente.

não não
fonte
3

Minha preferência em casos como esses é fazer computeOnee computeMoremétodos estáticos. O motivo: encapsulamento. Quanto menos código tiver acesso à implementação da sua classe, melhor.

No exemplo que você fornece, você declara que computeOnee computeMorenão deve acessar os internos da turma; portanto, por que dar a oportunidade aos mantenedores da turma se intrometerem com os internals?

maxaposteriori
fonte
3

Gostaria de esclarecer algumas coisas que outros pôsteres disseram como informações erradas.

Primeiro, como os métodos são privados, mesmo se você os declarar estáticos, não poderá acessá-los fora desta classe. Em segundo lugar, eles são privados, portanto você não pode substituir na subclasse, portanto, estático ou não estático, não faz diferença. Em terceiro lugar, um método privado não estático pode ser chamado de um construtor da classe também, não precisa ser estático.

Agora, vamos à sua pergunta se um método auxiliar privado deve ser definido como estático ou não estático. Eu irei com a resposta de Steve, pois marcar um método privado estático mostra que esse método é sem estado, pois também sigo essa regra quando codifico.

Bhushan Bhangale
fonte
mas há lugares onde apenas um método estático pode ser chamado, como em meu exemplo e no exemplo de Chris Marshall
Kip
Sim Kip your e Chris resposta estão corretas. Eu não comentei sobre eles, pois são respostas corretas. Eu só falei sobre respostas erradas.
Bhushan Bhangale 12/02/09
3

Pela experiência, eu afirmaria que esses métodos privados tendem a ser bastante universais e reutilizáveis.

Penso que a primeira coisa a fazer é perguntar se o método pode ser útil fora do contexto de classe atual. Nesse caso, eu faria exatamente o que Todos sugere e extrairia esse método como estático para algumas classes de utilitários, nas quais alguém espere verificar antes de implementar um novo método, fazendo exatamente a mesma coisa.

Tais métodos particulares de uso geral são fonte de grande parte da duplicação de código no projeto, porque cada desenvolvedor os reinventa independentemente no local em que precisa usá-lo. Portanto, a centralização de tais métodos é um caminho a percorrer.

Piotr Sobczyk
fonte
2

Mais especificamente para o exemplo que você deu, parece que o objetivo de definir esses métodos é mais para clareza de código quando você está lendo do que para funcionalidade (eles são definidos como privados). Nesse caso, usar a estática realmente não faz nada para você, pois o objetivo da estática é expor a funcionalidade da classe.

não não
fonte
Essa pequena resposta, escondida sob uma carga de outras respostas, realmente toca a essência do problema: funcionalidade em nível de classe versus funcionalidade em nível de objeto.
eljenso
Obrigado, el, eu realmente aprecio isso. Também não me importaria em votar um pouco :)
notnot 12/02/09
Eu marquei +1 no momento em que escrevi o comentário.
23310 eljenso
1
Discordo. A clareza do código ainda é importante para métodos privados. Pode ser menos importante, mas ainda é algo para se esforçar.
LegendLength
1

Uma razão é que, sendo tudo igual, as chamadas de método estático devem ser mais rápidas. Os métodos estáticos não podem ser virtuais e não levam uma referência implícita.

dsimcha
fonte
Veja minha resposta abaixo (eu adicionei a análise depois que ela já estava em -3, por isso não é muito visível).
Michael Myers
1

Como muitas pessoas disseram, faça como estática ! Aqui está a regra geral que sigo: Se você acha que o método é apenas uma função matemática, ou seja, sem estado, não envolve nenhuma variável de instância (=> sem cores azuis vars [no eclipse] no método) e o resultado de o método será o mesmo para o número 'n' de chamadas (com os mesmos parâmetros, é claro), depois marque esse método como STATIC.

E se você acha que esse método será útil para outra classe, mova-o para uma classe Util, caso contrário, coloque o método como privado na classe de amostra. (minimizando a acessibilidade)

jai
fonte
1

Fora do tópico: eu manteria os métodos auxiliares em uma classe autônoma de utilitário / auxiliar, apenas com métodos estáticos.

O problema de ter métodos auxiliares no ponto de uso (leia 'mesma classe') é que alguém abaixo da linha pode simplesmente optar por postar seus próprios métodos auxiliares não relacionados no mesmo local

Todos
fonte
-1: SO não é um fórum de mensagens. Você fará melhor fazendo uma pergunta adequada.
Stu Thompson
1
Eu preferiria ter os métodos auxiliares juntamente com a classe à qual eles estão vinculados / relacionados como estáticos. Ainda mais, posso expor alguns como públicos se eles devem ser usados ​​externamente. Dessa forma, será mais fácil manter a API para essa classe, se ela for exposta publicamente e usada intensivamente.
Ivaylo Slavov
1
class Whatever {

    public static varType myVar = initializeClassVariable();

    private static varType initializeClassVariable() {

        //initialization code goes here
    }
}

A vantagem dos métodos estáticos privados é que eles podem ser reutilizados posteriormente, se você precisar reinicializar a variável de classe.

mug896
fonte
1
  1. Sem o modificador estático, você não pode descobrir que o método é sem estado sem análise adicional, que pode ser feita facilmente quando você (re) escreve o método.

  2. O modificador "estático" pode fornecer idéias sobre refatoração, além de outras coisas que outros podem achar inúteis. Por exemplo, movendo o método para alguma classe Utility ou convertendo-o em um método membro.

bodrin
fonte
0

Eu os declararia estáticos para sinalizá-los como apátridas.

O Java não possui um mecanismo melhor para operações menores que não são exportadas, então acho que a estática privada é aceitável.

Uri
fonte