Qual é a vantagem de declarar um método como estático


94

Recentemente, estive olhando meus avisos no Eclipse e encontrei este:

aviso estático

Ele dará um aviso do compilador se o método puder ser declarado como estático.

[editar] Citação exata dentro da ajuda do Eclipse, com ênfase em privado e final:

Quando ativado, o compilador emitirá um erro ou um aviso para métodos que são privados ou finais e que se referem apenas a membros estáticos.

Sim, eu sei que posso desligá-lo, mas quero saber o motivo de ligá-lo.

Por que seria uma boa coisa declarar todos os métodos possíveis como estáticos?

Isso trará algum benefício de desempenho? (em um domínio móvel)

Apontar um método como estático, suponho que esteja mostrando que você não usa nenhuma variável de instância, portanto, poderia ser movido para uma classe de estilo utils?

No final do dia, devo apenas desligar "ignorar" ou devo corrigir os mais de 100 avisos que ele me deu?

Você acha que são apenas palavras-chave extras que sujam o código, já que o compilador apenas embutirá esses métodos de qualquer maneira? (como se você não declarasse todas as variáveis ​​que pode finalizar, mas poderia ).


Não tenho certeza, mas isso pode ser simplesmente visto como um auxílio de programação. Os avisos são simplesmente indicações de coisas a serem observadas.
James P.

1
Estou curioso para saber o tipo de funcionalidade que esses métodos executam. Talvez algo não tenha sido feito da maneira certa, se há tantos deles.
ArjunShankar

Respostas:


132

Sempre que você escreve um método, você cumpre um contrato em um determinado escopo. Quanto mais estreito for o escopo, menor será a chance de você escrever um bug.

Quando um método é estático, você não pode acessar membros não estáticos; portanto, seu escopo é mais estreito. Portanto, se você não precisa e nunca precisará (mesmo em subclasses) de membros não estáticos para cumprir seu contrato, por que conceder acesso a esses campos ao seu método? Declarar o método staticneste caso permitirá que o compilador verifique se você não usa membros que não pretende usar.

E, além disso, ajudará as pessoas que estão lendo seu código a entender a natureza do contrato.

É por isso que é considerado bom declarar um método staticquando ele está realmente implementando um contrato estático.

Em alguns casos, seu método significa apenas algo relativo a uma instância de sua classe, e acontece que sua implementação não usa nenhum campo ou instância não estático. Nesses casos, você não marcaria o método static.

Exemplos de onde você não usaria a staticpalavra-chave:

  • Um gancho de extensão que não faz nada (mas poderia fazer algo com dados de instância em uma subclasse)
  • Um comportamento padrão muito simples para ser personalizável em uma subclasse.
  • Implementação do manipulador de eventos: a implementação irá variar com a classe do manipulador de eventos, mas não usará nenhuma propriedade da instância do manipulador de eventos.

1
+1 Trata-se de minimizar as oportunidades de dar um tiro no próprio pé e reduzir o quanto você precisa saber para entender um método.
Peter Lawrey

7
Além disso, você não precisa se preocupar em adquirir uma instância apenas para chamar uma função independente e autocontida.
Marko Topolnik

1
Portanto, em outros mundos, qualquer método que não esteja usando variáveis ​​de instância deve ser declarado como estático? Sempre pensei que os métodos deveriam ser estáticos apenas se fosse desejável (como os métodos de utilidade).
Petr Mensik

18
@PetrMensik Se um privatemétodo pudesse ser declarado estático, quase invariavelmente deveria ser. Para qualquer outro nível de acesso, há outros fatores a serem considerados, como despacho dinâmico.
Marko Topolnik

1
@JamesPoulson, quero dizer despacho de método dinâmico, a coisa acontecendo enquanto tentamos object.method()selecionar o método a ser chamado.
Marko Topolnik

15

Não há conceito de otimização aqui.

Um staticmétodo ocorre staticporque você declara explicitamente que o método não depende de nenhuma instância da classe envolvente apenas porque não precisa. Portanto, esse aviso do Eclipse, conforme declarado na documentação:

Quando ativado, o compilador emitirá um erro ou um aviso para métodos que são privados ou finais e que se referem apenas a membros estáticos.

Se você não precisa de nenhuma variável de instância e seu método é privado (não pode ser chamado de fora) ou final (não pode ser substituído), então não há razão para permitir que seja um método normal ao invés de um estático. Um método estático é inerentemente mais seguro, mesmo porque você tem permissão para fazer menos coisas com ele (não precisa de nenhuma instância, você não tem nenhum thisobjeto implícito ).


7

Não tenho informações sobre o desempenho, suponho que seja um pouco melhor, no máximo, já que o código não precisa fazer despacho dinâmico com base no tipo.

No entanto, um argumento muito mais forte contra a refatoração em métodos estáticos é que atualmente o uso de estático é considerado uma prática ruim. Variáveis ​​/ métodos estáticos não se integram bem em uma linguagem orientada a objetos e também são difíceis de testar adequadamente. Esta é a razão pela qual algumas linguagens mais recentes abandonam o conceito de métodos / variáveis ​​estáticos completamente, ou tentam internalizá-lo na linguagem de uma forma que funcione melhor com OO (por exemplo, Objetos em Scala).

Na maioria das vezes, você precisa de métodos estáticos para implementar funções que estão usando apenas parâmetros como uma entrada e produzindo uma saída usando isso (por exemplo, funções de utilitário / auxiliar). Em linguagens modernas, há um conceito de função de primeira classe que permite isso, então estático Não é necessário. Java 8 terá expressões lambda integradas, portanto, já estamos avançando nessa direção.


7
Os métodos estáticos são fáceis de testar (contanto que sejam independentes - especialmente funções puras, que são o alvo principal de um método estático). O conceito OO não tem nada a oferecer nesse caso. Além disso, o conceito de função de primeira classe tem muito pouco, ou nada, a ver com o conceito de método estático. Se você precisar de uma função de primeira classe que faça o trabalho de um método estático existente, bastará um punhado de caracteres para implementá-la.
Marko Topolnik

5
A observação da testabilidade provavelmente não foi direcionada diretamente ao método estático, mas os métodos estáticos são muito mais difíceis de simular, portanto, eles complicam o teste de métodos que chamam métodos estáticos.
Buhb

2
Os métodos estáticos são fáceis de simular se você usar uma estrutura de simulação poderosa como JMockit , PowerMock ou Groovy .
Jeff Olson

Esses são private staticmétodos para que você não precise zombar deles
Blundell

3

1. Método de declaraçãostaticoferece um pequeno benefício de desempenho, mas o que é mais útil, permite usá-lo sem ter uma instância de objeto em mãos (pense, por exemplo, no método de fábrica ou na obtenção de um singleton). Também serve ao propósito documental de contar a natureza do método. Este propósito documental não deve ser ignorado, pois dá dicas imediatas sobre a natureza do método para os leitores do código e usuários da API e também serve como uma ferramenta de pensamento para o programador original - ser explícito sobre o significado pretendido ajuda você também pensa com clareza e produz código de melhor qualidade (acho que com base na minha experiência pessoal, mas as pessoas são diferentes). Por exemplo, é lógico e, portanto, desejável distinguir entre métodos que operam em um tipo e métodos que agem em uma instância do tipo (como apontado porJon Skeet em seu comentário a uma questão C # ).

Ainda outro caso de uso para staticmétodos é imitar a interface de programação procedural. Pense na java.lang.System.println()classe e nos métodos e atributos nela contidos. A classe java.lang.Systemé usada como um espaço de nomes de agrupamento, em vez de um objeto instanciável.

2. Como pode o Eclipse (ou qualquer outro tipo de entidade programada ou outro tipo de - biocomponível ou não biocomponível -) saber com certeza qual método pode ser declarado como estático? Mesmo se uma classe base não estiver acessando variáveis ​​de instância ou chamando métodos não estáticos, pelo mecanismo de herança as coisas podem mudar. Somente se o método não pode ser substituído pela subclasse herdada, podemos afirmar com 100% de certeza que o método realmente pode ser declarado static. Substituir um método é impossível exatamente nos dois casos de ser

  1. private (nenhuma subclasse pode usá-lo diretamente e nem mesmo em princípio sabe sobre ele), ou
  2. final (mesmo se acessível pela subclasse, não há como alterar o método para se referir a dados de instância ou funções).

Daí a lógica da opção Eclipse.

3. O autor da postagem original também pergunta: “ Apontar um método como estático, suponho que esteja mostrando que você não usa nenhuma variável de instância, portanto, poderia ser movido para uma classe de estilo utils? ” Este é um ponto muito bom. Às vezes, esse tipo de alteração de design é indicado pelo aviso.

É uma opção muito útil, que eu pessoalmente me certificaria de habilitar, se eu usasse o Eclipse e fosse programar em Java.


1

Veja a resposta de Samuel sobre como o escopo do método muda. Acho que esse é o aspecto principal de tornar um método estático.

Você também perguntou sobre o desempenho:

Pode haver um pequeno ganho de desempenho, porque uma chamada para um método estático não precisa da referência implícita "this" como parâmetro.

No entanto, esse impacto no desempenho é muito pequeno. Portanto, é tudo sobre o escopo.


1

Das diretrizes de desempenho do Android:

Prefira estático ao virtual Se você não precisa acessar os campos de um objeto, torne seu método estático. As invocações serão cerca de 15% -20% mais rápidas. Também é uma boa prática, porque você pode dizer pela assinatura do método que chamar o método não pode alterar o estado do objeto.

http://developer.android.com/training/articles/perf-tips.html#PreferStatic


"que chamar o método não pode alterar o estado do objeto" - é incrivelmente enganoso. Não sei sobre você, mas certamente considero os atributos estáticos de uma classe como parte do estado de um objeto.
Adam Parkin

0

Bem, a documentação do Eclipse diz sobre o aviso em questão:

Método pode ser estático

Quando ativado, o compilador emitirá um erro ou um aviso para métodos que são privados ou finais e que se referem apenas a membros estáticos

Acho que diz tudo. Se o método for privado e final e se referir apenas a membros estáticos, o método em questão pode muito bem ser declarado estático e, por isso, tornar evidente que pretendemos apenas acessar conteúdo estático a partir dele.

Sinceramente, não acho que haja qualquer outra razão misteriosa por trás disso.


0

Faltavam alguns números para as diferenças de velocidade. Então tentei fazer um benchmark, o que acabou não sendo tão fácil: o Java loop fica mais lento após algumas execuções / falha do JIT?

Finalmente usei o Caliper e os resultados são os mesmos de fazer meus testes manualmente:

Não há diferença mensurável para chamadas estáticas / dinâmicas. Pelo menos não para Linux / AMD64 / Java7.

Os resultados do Caliper estão aqui: https://microbenchmarks.appspot.com/runs/1426eac9-36ca-48f0-980f-0106af064e8f#r:scenario.benchmarkSpec.methodName,scenario.vmSpec.options.CMSLargeCoalSurplusPercent,Scenario.vm CMSLargeSplitSurplusPercent, scenery.vmSpec.options.CMSSmallCoalSurplusPercent, scenery.vmSpec.options.CMSSmallSplitSurplusPercent, scenery.vmSpec.options.FLSLargestBlockCoalesceProximity. scenery.varktionsSpecuration

e meus próprios resultados são:

Static: 352 ms
Dynamic: 353 ms
Static: 348 ms
Dynamic: 349 ms
Static: 349 ms
Dynamic: 348 ms
Static: 349 ms
Dynamic: 344 ms

A classe de teste Caliper foi:

public class TestPerfomanceOfStaticMethodsCaliper extends Benchmark {

    public static void main( String [] args ){

        CaliperMain.main( TestPerfomanceOfStaticMethodsCaliper.class, args );
    }

    public int timeAddDynamic( long reps ){
        int r=0;
        for( int i = 0; i < reps; i++ ) {
            r |= addDynamic( 1, i );
        }
        return r;
    }

    public int timeAddStatic( long reps ){
        int r=0;
        for( int i = 0; i < reps; i++ ) {
            r |= addStatic( 1, i );
        }
        return r;
    }

    public int addDynamic( int a, int b ){

        return a+b;
    }

    private static int addStatic( int a, int b ){

        return a+b;
    }

}

E minha própria classe de teste foi:

public class TestPerformanceOfStaticVsDynamicCalls {

    private static final int RUNS = 1_000_000_000;

    public static void main( String [] args ) throws Exception{

        new TestPerformanceOfStaticVsDynamicCalls().run();
    }

    private void run(){

        int r=0;
        long start, end;

        for( int loop = 0; loop<10; loop++ ){

            // Benchmark

            start = System.currentTimeMillis();
            for( int i = 0; i < RUNS; i++ ) {
                r += addStatic( 1, i );
            }
            end = System.currentTimeMillis();
            System.out.println( "Static: " + ( end - start ) + " ms" );

            start = System.currentTimeMillis();
            for( int i = 0; i < RUNS; i++ ) {
                r += addDynamic( 1, i );
            }
            end = System.currentTimeMillis();
            System.out.println( "Dynamic: " + ( end - start ) + " ms" );

            // Do something with r to keep compiler happy
            System.out.println( r );

        }

    }

    private int addDynamic( int a, int b ){

        return a+b;
    }

    private static int addStatic( int a, int b ){

        return a+b;
    }

}

seria interessante ver os resultados em vários dispositivos e versões Android
Blundell

Sim. Achei que você estaria interessado :) Mas como você está construindo um software Android e provavelmente tem um dispositivo Android conectado à sua estação de desenvolvimento, sugiro que você apenas escolha o código, execute-o e compartilhe os resultados.
Scheintod

-2

Os métodos que você pode declarar como estáticos são aqueles que não requerem instanciação, como

public class MyClass
{
    public static string InvertText(string text)
    {
        return text.Invert();
    }
}

Que você pode então chamar de volta em qualquer outra classe sem instanciar aquela classe.

public class MyClassTwo
{
    public void DoSomething()
    {
        var text = "hello world";
        Console.Write(MyClass.InvertText(text));
    }
}

... Mas isso é algo que você provavelmente já sabe. Ele não oferece nenhum benefício real per se, a não ser deixar mais claro que o método não usa nenhuma variável de instância.

Em outras palavras, você pode simplesmente desligá-lo completamente com segurança. Se você sabe que nunca usará um método em outras classes (nesse caso, ele deve ser apenas privado), você não precisa que ele seja estático.


1
Que linguagem é essa? Esse exemplo compila? Nada é estático aqui.
Piotr Perak

Na verdade, parece que esqueci 'estático' do método InvertText. E é um exemplo baseado em c #
NeroS
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.